Merge from Chromium at DEPS revision r198571

This commit was generated by merge_to_master.py.

Change-Id: I3a7f89ea6b8c017335bd52739166aed708cad1e5
diff --git a/Source/devtools/.gitignore b/Source/devtools/.gitignore
new file mode 100644
index 0000000..0ba62a5
--- /dev/null
+++ b/Source/devtools/.gitignore
@@ -0,0 +1,8 @@
+*.Makefile
+*.mk
+*.rules
+*.sln
+*.tmp
+*.vcproj*
+*.vcxproj*
+*.xcodeproj*
diff --git a/Source/devtools/Inspector-0.1.json b/Source/devtools/Inspector-0.1.json
new file mode 100644
index 0000000..a5c0303
--- /dev/null
+++ b/Source/devtools/Inspector-0.1.json
@@ -0,0 +1,2024 @@
+{
+    "compatible": [],
+    "domains" : [{
+        "domain": "Inspector",
+        "hidden": true,
+        "types": [],
+        "commands": [
+        ],
+        "events": [
+            {
+                "name": "frontendReused"
+            },
+            {
+                "name": "bringToFront"
+            },
+            {
+                "name": "disconnectFromBackend"
+            },
+            {
+                "name": "reset"
+            },
+            {
+                "name": "showPanel",
+                "parameters": [
+                    { "name": "panel", "type": "string" }
+                ]
+            },
+            {
+                "name": "startUserInitiatedDebugging"
+            },
+            {
+                "name": "evaluateForTestInFrontend",
+                "parameters": [
+                    { "name": "testCallId", "type": "integer" },
+                    { "name": "script", "type": "string" }
+                ]
+            },
+            {
+                "name": "inspect",
+                "parameters": [
+                    { "name": "object", "$ref": "Runtime.RemoteObject" },
+                    { "name": "hints", "type": "object" }
+                ]
+            },
+            {
+                "name": "didCreateWorker",
+                "parameters": [
+                    { "name": "id", "type": "integer" },
+                    { "name": "url", "type": "string" },
+                    { "name": "isShared", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "didDestroyWorker",
+                "parameters": [
+                    { "name": "id", "type": "integer" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "Page",
+        "description": "Actions and events related to the inspected page belong to the page domain.",
+        "types": [
+            {
+                "id": "ResourceType",
+                "type": "string",
+                "enum": ["Document", "Stylesheet", "Image", "Font", "Script", "XHR", "WebSocket", "Other"],
+                "description": "Resource type as it was perceived by the rendering engine."
+            },
+            {
+                "id": "Frame",
+                "type": "object",
+                "description": "Information about the Frame on the page.",
+                "properties": [
+                    { "name": "id", "type": "string", "description": "Frame unique identifier." },
+                    { "name": "parentId", "type": "string", "optional": true, "description": "Parent frame identifier." },
+                    { "name": "loaderId", "$ref": "Network.LoaderId", "description": "Identifier of the loader associated with this frame." },
+                    { "name": "name", "type": "string", "optional": true, "description": "Frame's name as specified in the tag." },
+                    { "name": "url", "type": "string", "description": "Frame document's URL." },
+                    { "name": "mimeType", "type": "string", "description": "Frame document's mimeType as determined by the browser." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "FrameResourceTree",
+                "type": "object",
+                "description": "Information about the Frame hierarchy along with their cached resources.",
+                "properties": [
+                    { "name": "frame", "$ref": "Frame", "description": "Frame information for this tree item." },
+                    { "name": "childFrames", "type": "array", "optional": true, "items": { "$ref": "FrameResourceTree" }, "description": "Child frames." },
+                    { "name": "resources", "type": "array",
+                        "items": {
+                            "type": "object",
+                            "properties": [
+                                { "name": "url", "type": "string", "description": "Resource URL." },
+                                { "name": "type", "$ref": "ResourceType", "description": "Type of this resource." },
+                                { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }
+                            ]
+                        },
+                        "description": "Information about frame resources."
+                    }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "SearchResult",
+                "type": "object",
+                "description": "Search result for resource.",
+                "properties": [
+	                { "name": "url", "type": "string", "description": "Resource URL." },
+                    { "name": "frameId", "type": "string", "description": "Resource frame id." },
+                    { "name": "matchesCount", "type": "number", "description": "Number of matches in the resource content." }
+                ],
+                "hidden": true
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables page domain notifications."
+            },
+            {
+                "name": "disable",
+                "description": "Disables page domain notifications."
+            },
+            {
+                "name": "addScriptToEvaluateOnLoad",
+                "parameters": [
+                    { "name": "scriptSource", "type": "string" }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "removeAllScriptsToEvaluateOnLoad",
+                "hidden": true
+            },
+            {
+                "name": "reload",
+                "parameters": [
+                    { "name": "ignoreCache", "type": "boolean", "optional": true, "description": "If true, browser cache is ignored (as if the user pressed Shift+refresh)." }
+                ],
+                "description": "Reloads given page optionally ignoring the cache."
+            },
+            {
+                "name": "open",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL to open." },
+                    { "name": "newWindow", "optional": true, "type": "boolean", "description": "If true, opens given URL in a new window or tab." }
+                ],
+                "description": "Opens given URL either in the inspected page or in a new tab / window.",
+                "hidden": true
+            },
+            {
+                "name": "getCookies",
+                "returns": [
+                    { "name": "cookies", "type": "array", "items": { "$ref": "Cookie"}, "description": "Array of cookie objects." },
+                    { "name": "cookiesString", "type": "string", "description": "document.cookie string representation of the cookies." }
+                ],
+                "description": "Returns all browser cookies. Depending on the backend support, will either return detailed cookie information in the <code>cookie</code> field or string cookie representation using <code>cookieString</code>.",
+                "hidden": true
+            },
+            {
+                "name": "deleteCookie",
+                "parameters": [
+                    { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." },
+                    { "name": "domain", "type": "string", "description": "Domain of the cookie to remove." }
+                ],
+                "description": "Deletes browser cookie with given name for the given domain.",
+                "hidden": true
+            },
+            {
+                "name": "getResourceTree",
+                "description": "Returns present frame / resource tree structure.",
+                "returns": [
+                    { "name": "frameTree", "$ref": "FrameResourceTree", "description": "Present frame / resource tree structure." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "getResourceContent",
+                "description": "Returns content of the given resource.",
+                "parameters": [
+                    { "name": "frameId", "type": "string", "description": "Frame id to get resource for." },
+                    { "name": "url", "type": "string", "description": "URL of the resource to get content for." }
+                ],
+                "returns": [
+                    { "name": "content", "type": "string", "description": "Resource content." },
+                    { "name": "base64Encoded", "type": "boolean", "description": "True, if content was served as base64." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "searchInResources",
+                "description": "Searches for given string in frame / resource tree structure.",
+                "parameters": [
+                    { "name": "text", "type": "string", "description": "String to search for."  },
+                    { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." },
+                    { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "SearchResult" }, "description": "List of search results." }
+                ],
+                "hidden": true
+            }
+        ],
+        "events": [
+            {
+                "name": "domContentEventFired",
+                "parameters": [
+                    { "name": "timestamp", "type": "number" }
+                ]
+            },
+            {
+                "name": "loadEventFired",
+                "parameters": [
+                    { "name": "timestamp", "type": "number" }
+                ]
+            },
+            {
+                "name": "frameNavigated",
+                "description": "Fired once navigation of the frame has completed. Frame is now associated with the new loader.",
+                "parameters": [
+                    { "name": "frame", "$ref": "Frame", "description": "Frame object." },
+                    { "name": "loaderId", "$ref": "Network.LoaderId", "description": "Loader identifier." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "frameDetached",
+                "description": "Fired when frame has been detached from its parent.",
+                "parameters": [
+                    { "name": "frameId", "type": "string", "description": "Id of the frame that has been detached." }
+                ],
+                "hidden": true
+            }
+        ]
+    },
+    {
+        "domain": "Runtime",
+        "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.",
+        "types": [
+            {
+                "id": "RemoteObjectId",
+                "type": "string",
+                "description": "Unique object identifier."
+            },
+            {
+                "id": "RemoteObject",
+                "type": "object",
+                "description": "Mirror object referencing original JavaScript object.",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type." },
+                    { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." },
+                    { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." },
+                    { "name": "value", "type": "any", "optional": true, "description": "Remote object value (in case of primitive values or JSON values if it was requested)." },
+                    { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." },
+                    { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }
+                ]
+            },
+            {
+                "id": "PropertyDescriptor",
+                "type": "object",
+                "description": "Object property descriptor.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Property name." },
+                    { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." },
+                    { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." },
+                    { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." },
+                    { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." },
+                    { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." },
+                    { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ]
+            },
+            {
+                "id": "CallArgument",
+                "type": "object",
+                "description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.",
+                "properties": [
+                    { "name": "value", "type": "any", "optional": true, "description": "Primitive value." },
+                    { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "evaluate",
+                "parameters": [
+                    { "name": "expression", "type": "string", "description": "Expression to evaluate." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." },
+                    { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true },
+                    { "name": "doNotPauseOnExceptions", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions. Overrides setPauseOnException state.", "hidden": true },
+                    { "name": "frameId", "type": "string", "optional": true, "description": "Specifies in which frame to perform evaluation.", "hidden": true },
+                    { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ],
+                "description": "Evaluates expression on global object."
+            },
+            {
+                "name": "callFunctionOn",
+                "parameters": [
+                    { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." },
+                    { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." },
+                    { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." },
+                    { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "RemoteObject", "description": "Call result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ],
+                "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object."
+            },
+            {
+                "name": "getProperties",
+                "parameters": [
+                    { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." },
+                    { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor"}, "description": "Object properties." }
+                ],
+                "description": "Returns properties of a given object. Object group of the result is inherited from the target object."
+            },
+            {
+                "name": "releaseObject",
+                "parameters": [
+                    { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." }
+                ],
+                "description": "Releases remote object with given id."
+            },
+            {
+                "name": "releaseObjectGroup",
+                "parameters": [
+                    { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." }
+                ],
+                "description": "Releases all remote objects that belong to a given group."
+            }
+        ]
+    },
+    {
+        "domain": "Console",
+        "description": "Console domain defines methods and events for interaction with the JavaScript console. Console collects messages created by means of the <a href='http://getfirebug.com/wiki/index.php/Console_API'>JavaScript Console API</a>. One needs to enable this domain using <code>enable</code> command in order to start receiving the console messages. Browser collects messages issued while console domain is not enabled as well and reports them using <code>messageAdded</code> notification upon enabling.",
+        "types": [
+            {
+                "id": "ConsoleMessage",
+                "type": "object",
+                "description": "Console message.",
+                "properties": [
+                    { "name": "source", "type": "string", "enum": ["html", "wml", "xml", "javascript", "network", "console-api", "other"], "description": "Message source." },
+                    { "name": "level", "type": "string", "enum": ["tip", "log", "warning", "error", "debug"], "description": "Message severity." },
+                    { "name": "text", "type": "string", "description": "Message text." },
+                    { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "trace", "startGroup", "startGroupCollapsed", "endGroup", "assert"], "description": "Console message type." },
+                    { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." },
+                    { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." },
+                    { "name": "repeatCount", "type": "integer", "optional": true, "description": "Repeat count for repeated messages." },
+                    { "name": "networkRequestId", "$ref": "Network.RequestId", "optional": true, "description": "Identifier of the network request associated with this message." },
+                    { "name": "parameters", "type": "array", "items": { "$ref": "Runtime.RemoteObject" }, "optional": true, "description": "Message parameters in case of the formatted message." },
+                    { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." }
+                ]
+            },
+            {
+                "id": "CallFrame",
+                "type": "object",
+                "description": "Stack entry for console errors and assertions.",
+                "properties": [
+                    { "name": "functionName", "type": "string", "description": "JavaScript function name." },
+                    { "name": "url", "type": "string", "description": "JavaScript script name or url." },
+                    { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number." },
+                    { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number." }
+                ]
+            },
+            {
+                "id": "StackTrace",
+                "type": "array",
+                "items": { "$ref": "CallFrame" },
+                "description": "Call frames for assertions or error messages."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "returns": [
+                    { "name": "expiredMessagesCount", "type": "integer",  "optional": true, "description": "Number of messages dropped due to message threashold overflow." }
+                ],
+                "description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification." 
+            },
+            {
+                "name": "disable",
+                "description": "Disables console domain, prevents further console messages from being reported to the client." 
+            },
+            {
+                "name": "clearMessages",
+                "description": "Clears console messages collected in the browser." 
+            },
+            {
+                "name": "setMonitoringXHREnabled",
+                "parameters": [
+                    { "name": "enabled", "type": "boolean", "description": "Monitoring enabled state." }
+                ],
+                "description": "Toggles monitoring of XMLHttpRequest. If <code>true</code>, console will receive messages upon each XHR issued.", 
+                "hidden": true
+            },
+            {
+                "name": "addInspectedNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "DOM node id to be accessible by means of $x command line API." }
+                ],
+                "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions).",
+                "hidden": true
+            }
+        ],
+        "events": [
+            {
+                "name": "messageAdded",
+                "parameters": [
+                    { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." }
+                ],
+                "description": "Issued when new console message is added."
+            },
+            {
+                "name": "messageRepeatCountUpdated",
+                "parameters": [
+                    { "name": "count", "type": "integer", "description": "New repeat count value." }
+                ],
+                "description": "Issued when subsequent message(s) are equal to the previous one(s)."
+            },
+            {
+                "name": "messagesCleared",
+                "description": "Issued when console is cleared. This happens either upon <code>clearMessages</code> command or after page navigation."
+            }
+        ]
+    },
+    {
+        "domain": "Network",
+        "description": "Network domain allows tracking network activities of the page. It exposes information about http, file, data and other requests and responses, their headers, bodies, timing, etc.",
+        "types": [
+            {
+                "id": "LoaderId",
+                "type": "string",
+                "description": "Unique loader identifier."
+            },
+            {
+                "id": "RequestId",
+                "type": "string",
+                "description": "Unique request identifier."
+            },
+            {
+                "id": "Timestamp",
+                "type": "number",
+                "description": "Number of seconds since epoch."
+            },
+            {
+                "id": "Headers",
+                "type": "object",
+                "description": "Request / response headers as keys / values of JSON object."
+            },
+            {
+                "id": "ResourceTiming",
+                "type": "object",
+                "description": "Timing information for the request.",
+                "properties": [
+                    { "name": "requestTime", "type": "number", "description": "Timing's requestTime is a baseline in seconds, while the other numbers are ticks in milliseconds relatively to this requestTime." },
+                    { "name": "proxyStart", "type": "number", "description": "Started resolving proxy." },
+                    { "name": "proxyEnd", "type": "number", "description": "Finished resolving proxy." },
+                    { "name": "dnsStart", "type": "number", "description": "Started DNS address resolve." },
+                    { "name": "dnsEnd", "type": "number", "description": "Finished DNS address resolve." },
+                    { "name": "connectStart", "type": "number", "description": "Started connecting to the remote host." },
+                    { "name": "connectEnd", "type": "number", "description": "Connected to the remote host." },
+                    { "name": "sslStart", "type": "number", "description": "Started SSL handshake." },
+                    { "name": "sslEnd", "type": "number", "description": "Finished SSL handshake." },
+                    { "name": "sendStart", "type": "number", "description": "Started sending request." },
+                    { "name": "sendEnd", "type": "number", "description": "Finished sending request." },
+                    { "name": "receiveHeadersEnd", "type": "number", "description": "Finished receiving response headers." }
+                ]
+            },
+            {
+                "id": "Request",
+                "type": "object",
+                "description": "HTTP request data.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Request URL." },
+                    { "name": "method", "type": "string", "description": "HTTP request method." },
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP request headers." },
+                    { "name": "postData", "type": "string", "optional": true, "description": "HTTP POST request data." }
+                ]
+            },
+            {
+                "id": "Response",
+                "type": "object",
+                "description": "HTTP response data.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Response URL." },
+                    { "name": "status", "type": "number", "description": "HTTP response status code." },
+                    { "name": "statusText", "type": "string", "description": "HTTP response status text." },
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." },
+                    { "name": "headersText", "type": "string", "optional": true, "description": "HTTP response headers text." },
+                    { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." },
+                    { "name": "requestHeaders", "$ref": "Headers", "optional": true, "description": "Refined HTTP request headers that were actually transmitted over the network." },
+                    { "name": "requestHeadersText", "type": "string", "optional": true, "description": "HTTP request headers text." },
+                    { "name": "connectionReused", "type": "boolean", "description": "Specifies whether physical connection was actually reused for this request." },
+                    { "name": "connectionId", "type": "number", "description": "Physical connection id that was actually used for this request." },
+                    { "name": "fromDiskCache", "type": "boolean", "optional": true, "description": "Specifies that the request was served from the disk cache." },
+                    { "name": "timing", "$ref": "ResourceTiming", "optional": true, "description": "Timing information for the given request." }
+                ]
+            },
+            {
+                "id": "CachedResource",
+                "type": "object",
+                "description": "Information about the cached resource.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Resource URL." },
+                    { "name": "type", "$ref": "Page.ResourceType", "description": "Type of this resource." },
+                    { "name": "response", "$ref": "Response", "optional": true, "description": "Cached response data." },
+                    { "name": "bodySize", "type": "number", "description": "Cached response body size." }
+                ]
+            },
+            {
+                "id": "Initiator",
+                "type": "object",
+                "description": "Information about the request initiator.",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["parser", "script", "other"], "description": "Type of this initiator." },
+                    { "name": "stackTrace", "$ref": "Console.StackTrace", "optional": true, "description": "Initiator JavaScript stack trace, set for Script only." },
+                    { "name": "url", "type": "string", "optional": true, "description": "Initiator URL, set for Parser type only." },
+                    { "name": "lineNumber", "type": "number", "optional": true, "description": "Initiator line number, set for Parser type only." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables network tracking, network events will now be delivered to the client."
+            },
+            {
+                "name": "disable",
+                "description": "Disables network tracking, prevents network events from being sent to the client."
+            },
+            {
+                "name": "setUserAgentOverride",
+                "description": "Allows overriding user agent with the given string.",
+                "parameters": [
+                    { "name": "userAgent", "type": "string", "description": "User agent to use." }
+                ]
+            },
+            {
+                "name": "setExtraHTTPHeaders",
+                "description": "Specifies whether to always send extra HTTP headers with the requests from this page.",
+                "parameters": [
+                    { "name": "headers", "$ref": "Headers", "description": "Map with extra HTTP headers." }
+                ]
+            },
+            {
+                "name": "getResponseBody",
+                "description": "Returns content served for the given request.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Identifier of the network request to get content for." }
+                ],
+                "returns": [
+                    { "name": "body", "type": "string", "description": "Response body." },
+                    { "name": "base64Encoded", "type": "boolean", "description": "True, if content was sent as base64." }
+                ]
+            },
+            {
+                "name": "clearBrowserCache",
+                "description": "Clears browser cache."
+            },
+            {
+                "name": "clearBrowserCookies",
+                "description": "Clears browser cookies."
+            },
+            {
+                "name": "setCacheDisabled",
+                "parameters": [
+                    { "name": "cacheDisabled", "type": "boolean", "description": "Cache disabled state." }
+                ],
+                "description": "Toggles ignoring cache for each request. If <code>true</code>, cache will not be used." 
+            }
+        ],
+        "events": [
+            {
+                "name": "requestWillBeSent",
+                "description": "Fired when page is about to send HTTP request.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "frameId", "type": "string", "description": "Frame identifier.", "hidden": true },
+                    { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." },
+                    { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." },
+                    { "name": "request", "$ref": "Request", "description": "Request data." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." },
+                    { "name": "stackTrace", "$ref": "Console.StackTrace", "optional": true, "description": "JavaScript stack trace upon issuing this request." },
+                    { "name": "redirectResponse", "optional": true, "$ref": "Response", "description": "Redirect response data." }
+                ]
+            },
+            {
+                "name": "requestServedFromCache",
+                "description": "Fired if request ended up loading from cache.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }
+                ]
+            },
+            {
+                "name": "responseReceived",
+                "description": "Fired when HTTP response is available.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "type", "$ref": "Page.ResourceType", "description": "Resource type." },
+                    { "name": "response", "$ref": "Response", "description": "Response data." }
+                ]
+            },
+            {
+                "name": "dataReceived",
+                "description": "Fired when data chunk was received over the network.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "dataLength", "type": "integer", "description": "Data chunk length." },
+                    { "name": "encodedDataLength", "type": "integer", "description": "Actual bytes received (might be less than dataLength for compressed encodings)." }
+                ]
+            },
+            {
+                "name": "loadingFinished",
+                "description": "Fired when HTTP request has finished loading.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }
+                ]
+            },
+            {
+                "name": "loadingFailed",
+                "description": "Fired when HTTP request has failed to load.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "errorText", "type": "string", "description": "User friendly error message." },
+                    { "name": "canceled", "type": "boolean", "optional": true, "description": "True if loading was canceled." }
+                ]
+            },
+            {
+                "name": "requestServedFromMemoryCache",
+                "description": "Fired when HTTP request has been served from memory cache.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "frameId", "type": "string", "description": "Frame identifier.", "hidden": true },
+                    { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." },
+                    { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." },
+                    { "name": "resource", "$ref": "CachedResource", "description": "Cached resource data." }
+                ]
+            },
+            {
+                "name": "webSocketWillSendHandshakeRequest",
+                "description": "Fired when WebSocket is about to initiate handshake.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "request", "type": "object", "description": "WebSocket request data." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketHandshakeResponseReceived",
+                "description": "Fired when WebSocket handshake response becomes available.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "response", "type": "object", "description": "WebSocket response data." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketCreated",
+                "description": "Fired upon WebSocket creation.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "url", "type": "string", "description": "WebSocket request URL." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketClosed",
+                "description": "Fired when WebSocket is closed.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }
+                ],
+                "hidden": true
+            }
+        ]
+    },
+    {
+        "domain": "Database",
+        "hidden": true,
+        "types": [
+            {
+                "id": "Database",
+                "type": "object",
+                "description": "Database object."
+            },
+            {
+                "id": "Error",
+                "type": "object",
+                "description": "Database error."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables database tracking, database events will now be delivered to the client."
+            },
+            {
+                "name": "disable",
+                "description": "Disables database tracking, prevents database events from being sent to the client."
+            },
+            {
+                "name": "getDatabaseTableNames",
+                "parameters": [
+                    { "name": "databaseId", "type": "integer" }
+                ],
+                "returns": [
+                    { "name": "tableNames", "type": "array", "items": { "type": "string" } }
+                ]
+            },
+            {
+                "name": "executeSQL",
+                "parameters": [
+                    { "name": "databaseId", "type": "integer" },
+                    { "name": "query", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "success", "type": "boolean" },
+                    { "name": "transactionId", "type": "integer" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "addDatabase",
+                "parameters": [
+                    { "name": "database", "$ref": "Database" }
+                ]
+            },
+            {
+                "name": "sqlTransactionSucceeded",
+                "parameters": [
+                    { "name": "transactionId", "type": "integer" },
+                    { "name": "columnNames", "type": "array", "items": { "type": "string" } },
+                    { "name": "values", "type": "array", "items": { "type": "string or number" }}
+                ]
+            },
+            {
+                "name": "sqlTransactionFailed",
+                "parameters": [
+                    { "name": "transactionId", "type": "integer" },
+                    { "name": "sqlError", "$ref": "Error" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "DOMStorage",
+        "hidden": true,
+        "types": [],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables storage tracking, storage events will now be delivered to the client."
+            },
+            {
+                "name": "disable",
+                "description": "Disables storage tracking, prevents storage events from being sent to the client."
+            },
+            {
+                "name": "getDOMStorageEntries",
+                "parameters": [
+                    { "name": "storageId", "type": "integer" }
+                ],
+                "returns": [
+                    { "name": "entries", "type": "array", "items": { "$ref": "DOMStorageEntry"} }
+                ]
+            },
+            {
+                "name": "setDOMStorageItem",
+                "parameters": [
+                    { "name": "storageId", "type": "integer" },
+                    { "name": "key", "type": "string" },
+                    { "name": "value", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "success", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "removeDOMStorageItem",
+                "parameters": [
+                    { "name": "storageId", "type": "integer" },
+                    { "name": "key", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "success", "type": "boolean" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "addDOMStorage",
+                "parameters": [
+                    { "name": "storage", "type": "object" }
+                ]
+            },
+            {
+                "name": "updateDOMStorage",
+                "parameters": [
+                    { "name": "storageId", "type": "integer" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "ApplicationCache",
+        "hidden": true,
+        "types": [
+            {
+                "id": "AppCache",
+                "type": "object",
+                "description": "AppCache."
+            }
+        ],
+        "commands": [
+            {
+                "name": "getApplicationCaches",
+                "returns": [
+                    { "name": "applicationCaches", "$ref": "AppCache" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "updateApplicationCacheStatus",
+                "parameters": [
+                    { "name": "status", "type": "integer" }
+                ]
+            },
+            {
+                "name": "updateNetworkState",
+                "parameters": [
+                    { "name": "isNowOnline", "type": "boolean" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "DOM",
+        "description": "This domain exposes DOM read/write operations. Each DOM Node is represented with its mirror object that has an <code>id</code>. This <code>id</code> can be used to get additional information on the Node, resolve it into the JavaScript object wrapper, etc. It is important that client receives DOM events only for the nodes that are known to the client. Backend keeps track of the nodes that were sent to the client and never sends the same node twice. It is client's responsibility to collect information about the nodes that were sent to the client.<p>Note that <code>iframe</code> owner elements will return corresponding document elements as their child nodes.</p>",
+        "types": [
+            {
+                "id": "NodeId",
+                "type": "integer",
+                "description": "Unique DOM node identifier."
+            },
+            {
+                "id": "Node",
+                "type": "object",
+                "properties": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Node identifier that is passed into the rest of the DOM messages as the <code>nodeId</code>. Backend will only push node with given <code>id</code> once. It is aware of all requested nodes and will only fire DOM events for nodes known to the client." },
+                    { "name": "nodeType", "type": "integer", "description": "<code>Node</code>'s nodeType." },
+                    { "name": "nodeName", "type": "string", "description": "<code>Node</code>'s nodeName." },
+                    { "name": "localName", "type": "string", "description": "<code>Node</code>'s localName." },
+                    { "name": "nodeValue", "type": "string", "description": "<code>Node</code>'s nodeValue." },
+                    { "name": "childNodeCount", "type": "integer", "optional": true, "description": "Child count for <code>Container</code> nodes." },
+                    { "name": "children", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Child nodes of this node when requested with children." },
+                    { "name": "attributes", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Attributes of the <code>Element</code> node in the form of flat array <code>[name1, value1, name2, value2]</code>." },
+                    { "name": "documentURL", "type": "string", "optional": true, "description": "Document URL that <code>Document</code> or <code>FrameOwner</code> node points to." },
+                    { "name": "publicId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s publicId." },
+                    { "name": "systemId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s systemId." },
+                    { "name": "internalSubset", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s internalSubset." },
+                    { "name": "name", "type": "string", "optional": true, "description": "<code>Attr</code>'s name." },
+                    { "name": "value", "type": "string", "optional": true, "description": "<code>Attr</code>'s value." }
+                ],
+                "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type."
+            },
+            {
+                "id": "EventListener",
+                "type": "object",
+                "hidden": true,
+                "properties": [
+                    { "name": "type", "type": "string", "description": "<code>EventListener</code>'s type." },
+                    { "name": "useCapture", "type": "boolean", "description": "<code>EventListener</code>'s useCapture." },
+                    { "name": "isAttribute", "type": "boolean", "description": "<code>EventListener</code>'s isAttribute." },
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Target <code>DOMNode</code> id." },
+                    { "name": "handlerBody", "type": "string", "description": "Event handler function body." },
+                    { "name": "location", "$ref": "Debugger.Location", "optional": true, "description": "Handler code location." }
+                ],
+                "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type."
+            },
+            {
+                "id": "RGBA",
+                "type": "object",
+                "properties": [
+                    { "name": "r", "type": "integer", "description": "The red component, in the [0-255] range." },
+                    { "name": "g", "type": "integer", "description": "The green component, in the [0-255] range." },
+                    { "name": "b", "type": "integer", "description": "The blue component, in the [0-255] range." },
+                    { "name": "a", "type": "number", "optional": true, "description": "The alpha component, in the [0-1] range (default: 1)." }
+                ],
+                "description": "A structure holding an RGBA color."
+            },
+            {
+                "id": "HighlightConfig",
+                "type": "object",
+                "properties": [
+                    { "name": "showInfo", "type": "boolean", "optional": true, "description": "Whether the node info tooltip should be shown (default: false)." },
+                    { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." },
+                    { "name": "paddingColor", "$ref": "RGBA", "optional": true, "description": "The padding highlight fill color (default: transparent)." },
+                    { "name": "borderColor", "$ref": "RGBA", "optional": true, "description": "The border highlight fill color (default: transparent)." },
+                    { "name": "marginColor", "$ref": "RGBA", "optional": true, "description": "The margin highlight fill color (default: transparent)." }
+                ],
+                "description": "Configuration data for the highlighting of page elements."
+            }
+        ],
+        "commands": [
+            {
+                "name": "getDocument",
+                "returns": [
+                    { "name": "root", "$ref": "Node", "description": "Resulting node." }
+                ],
+                "description": "Returns the root DOM node to the caller."
+            },
+            {
+                "name": "requestChildNodes",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get children for." }
+                ],
+                "description": "Requests that children of the node with given id are returned to the caller in form of <code>setChildNodes</code> events."
+            },
+            {
+                "name": "querySelector",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." },
+                    { "name": "selector", "type": "string", "description": "Selector string." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Query selector result." }
+                ],
+                "description": "Executes <code>querySelector</code> on a given node."
+            },
+            {
+                "name": "querySelectorAll",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." },
+                    { "name": "selector", "type": "string", "description": "Selector string." }
+                ],
+                "returns": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Query selector result." }
+                ],
+                "description": "Executes <code>querySelectorAll</code> on a given node."
+            },
+            {
+                "name": "setNodeName",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set name for." },
+                    { "name": "name", "type": "string", "description": "New node's name." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "New node's id." }
+                ],
+                "description": "Sets node name for a node with given id."
+            },
+            {
+                "name": "setNodeValue",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set value for." },
+                    { "name": "value", "type": "string", "description": "New node's value." }
+                ],
+                "description": "Sets node value for a node with given id."
+            },
+            {
+                "name": "removeNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to remove." }
+                ],
+                "description": "Removes node with given id."
+            },
+            {
+                "name": "setAttributeValue",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attribute for." },
+                    { "name": "name", "type": "string", "description": "Attribute name." },
+                    { "name": "value", "type": "string", "description": "Attribute value." }
+                ],
+                "description": "Sets attribute for an element with given id."
+            },
+            {
+                "name": "setAttributesAsText",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attributes for." },
+                    { "name": "text", "type": "string", "description": "Text with a number of attributes. Will parse this text using HTML parser." },
+                    { "name": "name", "type": "string", "optional": true, "description": "Attribute name to replace with new attributes derived from text in case text parsed successfully." }
+                ],
+                "description": "Sets attributes on element with given id. This method is useful when user edits some existing attribute value and types in several attribute name/value pairs."
+            },
+            {
+                "name": "removeAttribute",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to remove attribute from." },
+                    { "name": "name", "type": "string", "description": "Name of the attribute to remove." }
+                ],
+                "description": "Removes attribute with given name from an element with given id."
+            },
+            {
+                "name": "getEventListenersForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get listeners for." }
+                ],
+                "returns": [
+                    { "name": "listeners", "type": "array", "items": { "$ref": "EventListener"}, "description": "Array of relevant listeners." }
+                ],
+                "description": "Returns event listeners relevant to the node.",
+                "hidden": true
+            },
+            {
+                "name": "copyNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to copy." }
+                ],
+                "description": "Copies node's HTML markup into the clipboard.",
+                "hidden": true
+            },
+            {
+                "name": "getOuterHTML",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get markup for." }
+                ],
+                "returns": [
+                    { "name": "outerHTML", "type": "string", "description": "Outer HTML markup." }
+                ],
+                "description": "Returns node's HTML markup."
+            },
+            {
+                "name": "setOuterHTML",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set markup for." },
+                    { "name": "outerHTML", "type": "string", "description": "Outer HTML markup to set." }
+                ],
+                "description": "Sets node HTML markup, returns new node id."
+            },
+            {
+                "name": "performSearch",
+                "parameters": [
+                    { "name": "query", "type": "string", "description": "Plain text or query selector or XPath search query." },
+                    { "name": "runSynchronously", "type": "boolean", "optional": true, "description": "When set to true, performing search synchronously (can block user interaction)." }
+                ],
+                "description": "Starts asynchronous search for a given string in the DOM tree. Use <code>cancelSearch</code> to stop given asynchronous search task.",
+                "hidden": true
+            },
+            {
+                "name": "cancelSearch",
+                "description": "Cancels asynchronous search started with <code>performSearch</code>.",
+                "hidden": true
+            },
+            {
+                "name": "requestNode",
+                "parameters": [
+                    { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "JavaScript object id to convert into node." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Node id for given object." }
+                ],
+                "description": "Requests that the node is sent to the caller given the JavaScript node object reference. All nodes that form the path from the node to the root are also sent to the client as a series of <code>setChildNodes</code> notifications."
+            },
+            {
+                "name": "setInspectModeEnabled",
+                "hidden": true,
+                "parameters": [
+                    { "name": "enabled", "type": "boolean", "description": "True to enable inspection mode, false to disable it." },
+                    { "name": "highlightConfig", "$ref": "HighlightConfig", "optional": true, "description": "A descriptor for the highlight appearance of hovered-over nodes. May be omitted if <code>enabled == false</code>." }
+                ],
+                "description": "Enters the 'inspect' mode. In this mode, elements that user is hovering over are highlighted. Backend then generates 'inspect' command upon element selection."
+            },
+            {
+                "name": "highlightRect",
+                "parameters": [
+                    { "name": "x", "type": "integer", "description": "X coordinate" },
+                    { "name": "y", "type": "integer", "description": "Y coordinate" },
+                    { "name": "width", "type": "integer", "description": "Rectangle width" },
+                    { "name": "height", "type": "integer", "description": "Rectangle height" },
+                    { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." },
+                    { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." }
+                ],
+                "description": "Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport."
+            },
+            {
+                "name": "highlightNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Identifier of the node to highlight." },
+                    { "name": "highlightConfig", "$ref": "HighlightConfig", "description": "A descriptor for the highlight appearance." }
+                ],
+                "description": "Highlights DOM node with given id."
+            },
+            {
+                "name": "hideHighlight",
+                "description": "Hides DOM node highlight."
+            },
+            {
+                "name": "highlightFrame",
+                "parameters": [
+                    { "name": "frameId", "type": "string", "description": "Identifier of the frame to highlight." },
+                    { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." },
+                    { "name": "contentOutlineColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight outline color (default: transparent)." }
+                ],
+                "description": "Highlights owner element of the frame with given id.",
+                "hidden": true
+            },
+            {
+                "name": "pushNodeByPathToFrontend",
+                "parameters": [
+                    { "name": "path", "type": "string", "description": "Path to node in the proprietary format." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node for given path." }
+                ],
+                "description": "Requests that the node is sent to the caller given its path. // FIXME, use XPath",
+                "hidden": true
+            },
+            {
+                "name": "resolveNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to resolve." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }
+                ],
+                "returns": [
+                    { "name": "object", "$ref": "Runtime.RemoteObject", "description": "JavaScript object wrapper for given node." }
+                ],
+                "description": "Resolves JavaScript node object for given node id."
+            },
+            {
+                "name": "getAttributes",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to retrieve attibutes for." }
+                ],
+                "returns": [
+                    { "name": "attributes", "type": "array", "items": { "type": "string" }, "description": "An interleaved array of node attribute names and values." }
+                ],
+                "description": "Returns attributes for the specified node."
+            },
+            {
+                "name": "moveTo",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to drop." },
+                    { "name": "targetNodeId", "$ref": "NodeId", "description": "Id of the element to drop into." },
+                    { "name": "insertBeforeNodeId", "$ref": "NodeId", "optional": true, "description": "Drop node before given one." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "New id of the moved node." }
+                ],
+                "description": "Moves node into the new container, places it before the given anchor."
+            }
+        ],
+        "events": [
+            {
+                "name": "documentUpdated",
+                "description": "Fired when <code>Document</code> has been totally updated. Node ids are no longer valid."
+            },
+            {
+                "name": "setChildNodes",
+                "parameters": [
+                    { "name": "parentId", "$ref": "NodeId", "description": "Parent node id to populate with children." },
+                    { "name": "nodes", "type": "array", "items": { "$ref": "Node"}, "description": "Child nodes array." }
+                ],
+                "description": "Fired when backend wants to provide client with the missing DOM structure. This happens upon most of the calls requesting node ids."
+            },
+            {
+                "name": "attributeModified",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "name", "type": "string", "description": "Attribute name." },
+                    { "name": "value", "type": "string", "description": "Attribute value." }
+                ],
+                "description": "Fired when <code>Element</code>'s attribute is modified."
+            },
+            {
+                "name": "attributeRemoved",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "name", "type": "string", "description": "Local attribute name." }
+                ],
+                "description": "Fired when <code>Element</code>'s attribute is removed."
+            },
+            {
+                "name": "inlineStyleInvalidated",
+                "parameters": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the nodes for which the inline styles have been invalidated." }
+                ],
+                "description": "Fired when <code>Element</code>'s inline style is modified via a CSS property modification.",
+                "hidden": true
+            },
+            {
+                "name": "characterDataModified",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "characterData", "type": "string", "description": "New text value." }
+                ],
+                "description": "Mirrors <code>DOMCharacterDataModified</code> event."
+            },
+            {
+                "name": "childNodeCountUpdated",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "childNodeCount", "type": "integer", "description": "New node count." }
+                ],
+                "description": "Fired when <code>Container</code>'s child node count has changed."
+            },
+            {
+                "name": "childNodeInserted",
+                "parameters": [
+                    { "name": "parentNodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "previousNodeId", "$ref": "NodeId", "description": "If of the previous siblint." },
+                    { "name": "node", "$ref": "Node", "description": "Inserted node data." }
+                ],
+                "description": "Mirrors <code>DOMNodeInserted</code> event."
+            },
+            {
+                "name": "childNodeRemoved",
+                "parameters": [
+                    { "name": "parentNodeId", "$ref": "NodeId", "description": "Parent id." },
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has been removed." }
+                ],
+                "description": "Mirrors <code>DOMNodeRemoved</code> event."
+            },
+            {
+                "name": "searchResults",
+                "parameters": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the search result nodes." }
+                ],
+                "description": "Pushes search results initiated using <code>performSearch</code> to the client.",
+                "hidden": true
+            }
+        ]
+    },
+    {
+        "domain": "CSS",
+        "hidden": true,
+        "description": "This domain exposes CSS read/write operations. All CSS objects, like stylesheets, rules, and styles, have an associated <code>id</code> used in subsequent operations on the related object. Each object type has a specific <code>id</code> structure, and those are not interchangeable between objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls (which accept a DOM node id). Alternatively, a client can discover all the existing stylesheets with the <code>getAllStyleSheets()</code> method and subsequently load the required stylesheet contents using the <code>getStyleSheet[Text]()</code> methods.",
+        "types": [
+            {
+                "id": "CSSStyleId",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "type": "string", "description": "Enclosing stylesheet identifier." },
+                    { "name": "ordinal", "type": "integer", "description": "The style ordinal within the stylesheet." }
+                ],
+                "description": "This object identifies a CSS style in a unique way."
+            },
+            {
+                "id": "CSSRuleId",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "type": "string", "description": "Enclosing stylesheet identifier." },
+                    { "name": "ordinal", "type": "integer", "description": "The rule ordinal within the stylesheet." }
+                ],
+                "description": "This object identifies a CSS rule in a unique way."
+            },
+            {
+                "id": "CSSNodeStyles",
+                "type": "object",
+                "properties": [
+                    { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "The node's inline style, if any." },
+                    { "name": "computedStyle", "$ref": "CSSComputedStyle", "description": "The node's computed style." },
+                    { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "CSS rules matching this node, from all applicable stylesheets." },
+                    { "name": "styleAttributes", "type": "array", "items": { "$ref": "CSSStyleAttribute" }, "description": "Entries for style-related element attributes (e.g. width=20)."},
+                    { "name": "pseudoElements", "type": "array", "items": { "$ref": "PseudoIdRules" }, "description": "Pseudo style rules for this node." },
+                    { "name": "inherited", "type": "array", "items": { "$ref": "InheritedStyleEntry" }, "description": "A chain of inherited styles (from the immediate node parent up to the DOM tree root)." }
+                ],
+                "description": "A holder for all CSS styles applicable to a particular DOM node."
+            },
+            {
+                "id": "PseudoIdRules",
+                "type": "object",
+                "properties": [
+                    { "name": "pseudoId", "type": "integer", "description": "Pseudo style identifier (see <code>enum PseudoId</code> in <code>RenderStyleConstants.h</code>)."},
+                    { "name": "rules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "CSS rules applicable to the pseudo style."}
+                ],
+                "description": "CSS rule collection for a single pseudo style."
+            },
+            {
+                "id": "InheritedStyleEntry",
+                "type": "object",
+                "properties": [
+                    { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "The ancestor node's inline style, if any, in the style inheritance chain." },
+                    { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "CSS rules matching the ancestor node in the style inheritance chain." }
+                ],
+                "description": "CSS rule collection for a single pseudo style."
+            },
+            {
+                "id": "CSSStyleAttribute",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "DOM attribute name (e.g. \"width\")."},
+                    { "name": "style", "$ref": "CSSStyle", "description": "CSS style generated by the respective DOM attribute."}
+                ],
+                "description": "CSS style information for a DOM style attribute."
+            },
+            {
+                "id": "CSSStyleSheetHeader",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "type": "string", "description": "The stylesheet identifier."},
+                    { "name": "sourceURL", "type": "string", "description": "Stylesheet resource URL."},
+                    { "name": "title", "type": "string", "description": "Stylesheet title."},
+                    { "name": "disabled", "type": "boolean", "description": "Denotes whether the stylesheet is disabled."}
+                ],
+                "description": "CSS stylesheet metainformation."
+            },
+            {
+                "id": "CSSStyleSheetBody",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "type": "string", "description": "The stylesheet identifier."},
+                    { "name": "rules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "Stylesheet resource URL."},
+                    { "name": "text", "type": "string", "optional": true, "description": "Stylesheet resource contents (if available)."}
+                ],
+                "description": "CSS stylesheet contents."
+            },
+            {
+                "id": "CSSRule",
+                "type": "object",
+                "properties": [
+                    { "name": "ruleId", "$ref": "CSSRuleId", "description": "The CSS rule identifier."},
+                    { "name": "selectorText", "type": "string", "description": "Rule selector."},
+                    { "name": "sourceURL", "type": "string", "optional": true, "description": "Parent stylesheet resource URL (for regular rules)."},
+                    { "name": "sourceLine", "type": "integer", "description": "Line ordinal of the rule selector start character in the resource."},
+                    { "name": "origin", "type": "string", "enum": ["user", "user-agent", "inspector", ""], "description": "The parent stylesheet type: \"user\" for user stylesheets, \"user-agent\" for user-agent stylesheets, \"inspector\" for stylesheets created by the inspector (i.e. those holding new rules created with <code>addRule()</code>), \"\" for regular stylesheets."},
+                    { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." },
+                    { "name": "selectorRange", "$ref": "SourceRange", "optional": true, "description": "The rule selector range in the underlying resource (if available)." }
+                ],
+                "description": "CSS rule representation."
+            },
+            {
+                "id": "SourceRange",
+                "type": "object",
+                "properties": [
+                    { "name": "start", "type": "integer", "description": "Start of range (inclusive)."},
+                    { "name": "end", "type": "integer", "description": "End of range (exclusive)."}
+                ],
+                "description": "Text range within a resource."
+            },
+            {
+                "id": "ShorthandEntry",
+                "type": "object"
+            },
+            {
+                "id": "CSSComputedStyle",
+                "type": "object"
+            },            
+            {
+                "id": "CSSStyle",
+                "type": "object",
+                "properties": [
+                    { "name": "styleId", "$ref": "CSSStyleId", "description": "The CSS style identifier."},
+                    { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSProperty" }, "description": "CSS properties in the style."},
+                    { "name": "shorthandEntries", "type": "array", "items": { "$ref": "ShorthandEntry" }, "description": "Computed values for all shorthands found in the style." },
+                    { "name": "cssText", "type": "string", "optional": true, "description": "Style declaration text (if available)."},
+                    { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Style declaration range in the enclosing stylesheet (if available)." },
+                    { "name": "width", "type": "integer", "description": "The effective \"width\" property value from this style." },
+                    { "name": "height", "type": "integer", "description": "The effective \"height\" property value from this style." }
+                ],
+                "description": "CSS style representation."
+            },
+            {
+                "id": "CSSProperty",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "The property name." },
+                    { "name": "value", "type": "string", "description": "The property value." },
+                    { "name": "priority", "type": "string", "optional": true, "description": "The property priority (implies \"\" if absent)." },
+                    { "name": "implicit", "type": "boolean", "optional": true, "description": "Whether the property is implicit (implies <code>false</code> if absent)." },
+                    { "name": "parsedOk", "type": "boolean", "optional": true, "description": "Whether the property is understood by the browser (implies <code>true</code> if absent)." },
+                    { "name": "status", "type": "string", "enum": ["active", "inactive", "disabled", "style"], "optional": true, "description": "The property status: \"active\" (implied if absent) if the property is effective in the style, \"inactive\" if the property is overridden by a same-named property in this style later on, \"disabled\" if the property is disabled by the user, \"style\" if the property is reported by the browser rather than by the CSS source parser." },
+                    { "name": "shorthandName", "type": "string", "description": "The related shorthand property name (absent if this property is not a longhand)." },
+                    { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The entire property range in the enclosing style declaration (if available)." }
+                ],
+                "description": "CSS style effective visual dimensions and source offsets."
+            }
+        ],
+        "commands": [
+            {
+                "name": "getStylesForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId" },
+                    { "name": "forcedPseudoClasses", "type": "array", "items": { "type": "string", "enum": ["active", "focus", "hover", "visited"] }, "optional": true, "description": "Element pseudo classes to force when computing applicable style rules." }
+                ],
+                "returns": [
+                    { "name": "styles", "$ref": "CSSNodeStyles", "description": "All styles for the specified DOM node." }
+                ],
+                "description": "Returns all styles for a DOM node identified by <code>nodeId</code>."
+            },
+            {
+                "name": "getComputedStyleForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId" }
+                ],
+                "returns": [
+                    { "name": "style", "$ref": "CSSStyle", "description": "Computed style for the specified DOM node." }
+                ],
+                "description": "Returns the computed style for a DOM node identified by <code>nodeId</code>."
+            },
+            {
+                "name": "getInlineStyleForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId" }
+                ],
+                "returns": [
+                    { "name": "style", "$ref": "CSSStyle", "description": "Inline style for the specified DOM node." }
+                ],
+                "description": "Returns the inline style (if present) for a DOM node identified by <code>nodeId</code>."
+            },
+            {
+                "name": "getAllStyleSheets",
+                "returns": [
+                    { "name": "headers", "type": "array", "items": { "$ref": "CSSStyleSheetHeader" }, "description": "Descriptor entries for all available stylesheets." }
+                ],
+                "description": "Returns metainfo entries for all known stylesheets."
+            },
+            {
+                "name": "getStyleSheet",
+                "parameters": [
+                    { "name": "styleSheetId", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "styleSheet", "$ref": "CSSStyleSheetBody", "description": "Stylesheet contents for the specified <code>styleSheetId</code>." }
+                ],
+                "description": "Returns stylesheet data for the specified <code>styleSheetId</code>."
+            },
+            {
+                "name": "getStyleSheetText",
+                "parameters": [
+                    { "name": "styleSheetId", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "text", "type": "string", "description": "The stylesheet text." }
+                ],
+                "description": "Returns the current textual content and the URL for a stylesheet."
+            },
+            {
+                "name": "setStyleSheetText",
+                "parameters": [
+                    { "name": "styleSheetId", "type": "string" },
+                    { "name": "text", "type": "string" }
+                ],
+                "description": "Sets the new stylesheet text, thereby invalidating all existing <code>CSSStyleId</code>'s and <code>CSSRuleId</code>'s contained by this stylesheet."
+            },
+            {
+                "name": "setPropertyText",
+                "parameters": [
+                    { "name": "styleId", "$ref": "CSSStyleId" },
+                    { "name": "propertyIndex", "type": "integer" },
+                    { "name": "text", "type": "string" },
+                    { "name": "overwrite", "type": "boolean" }
+                ],
+                "returns": [
+                    { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property text modification." }
+                ],
+                "description": "Sets the new <code>text</code> for a property in the respective style, at offset <code>propertyIndex</code>. If <code>overwrite</code> is <code>true</code>, a property at the given offset is overwritten, otherwise inserted. <code>text</code> entirely replaces the property <code>name: value</code>."
+            },
+            {
+                "name": "toggleProperty",
+                "parameters": [
+                    { "name": "styleId", "$ref": "CSSStyleId" },
+                    { "name": "propertyIndex", "type": "integer" },
+                    { "name": "disable", "type": "boolean" }
+                ],
+                "returns": [
+                    { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property toggling." }
+                ],
+                "description": "Toggles the property in the respective style, at offset <code>propertyIndex</code>. The <code>disable</code> parameter denotes whether the property should be disabled (i.e. removed from the style declaration). If <code>disable == false</code>, the property gets put back into its original place in the style declaration."
+            },
+            {
+                "name": "setRuleSelector",
+                "parameters": [
+                    { "name": "ruleId", "$ref": "CSSRuleId" },
+                    { "name": "selector", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "rule", "$ref": "CSSRule", "description": "The resulting rule after the selector modification." }
+                ],
+                "description": "Modifies the rule selector."
+            },
+            {
+                "name": "addRule",
+                "parameters": [
+                    { "name": "contextNodeId", "$ref": "DOM.NodeId" },
+                    { "name": "selector", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "rule", "$ref": "CSSRule", "description": "The newly created rule." }
+                ],
+                "description": "Creates a new empty rule with the given <code>selector</code> in a special \"inspector\" stylesheet in the owner document of the context node."
+            },
+            {
+                "name": "getSupportedCSSProperties",
+                "returns": [
+                    { "name": "cssProperties", "type": "array", "items": { "type": "string" }, "description": "Supported property names." }
+                ],
+                "description": "Returns all supported CSS property names."
+            }
+        ]
+    },
+    {
+        "domain": "Timeline",
+        "description": "Timeline provides its clients with instrumentation records that are generated during the page runtime. Timeline instrumentation can be started and stopped using corresponding commands. While timeline is started, it is generating timeline event records.",
+        "types": [
+            {
+                "id": "TimelineEvent",
+                "type": "object",
+                "properties": [
+                    { "name": "type", "type": "string", "description": "Event type." },
+                    { "name": "data", "type": "object", "description": "Event data." },
+                    { "name": "children", "type": "array", "optional": true, "items": { "$ref": "TimelineEvent" }, "description": "Nested records." }
+                ],
+                "description": "Timeline record contains information about the recorded activity."
+            }
+        ],
+        "commands": [
+            {
+                "name": "start",
+                "parameters": [
+                    { "name": "maxCallStackDepth", "optional": true, "type": "integer", "description": "Samples JavaScript stack traces up to <code>maxCallStackDepth</code>, defaults to 5." }
+                ],
+                "description": "Starts capturing instrumentation events."
+            },
+            {
+                "name": "stop",
+                "description": "Stops capturing instrumentation events."
+            }
+        ],
+        "events": [
+            {
+                "name": "started",
+                "description": "Fired when timeline has been started.",
+                "hidden": true
+            },
+            {
+                "name": "stopped",
+                "description": "Fired when timeline has been stopped.",
+                "hidden": true
+            },
+            {
+                "name": "eventRecorded",
+                "parameters": [
+                    { "name": "record", "$ref": "TimelineEvent", "description": "Timeline event record data." }
+                ],
+                "description": "Fired for every instrumentation event while timeline is started."
+            }
+        ]
+    },
+    {
+        "domain": "Debugger",
+        "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.",
+        "types": [
+            {
+                "id": "BreakpointId",
+                "type": "string",
+                "description": "Breakpoint identifier."
+            },
+            {
+                "id": "ScriptId",
+                "type": "string",
+                "description": "Unique script identifier."
+            },
+            {
+                "id": "CallFrameId",
+                "type": "string",
+                "description": "Call frame identifier."
+            },
+            {
+                "id": "Location",
+                "type": "object",
+                "properties": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." },
+                    { "name": "lineNumber", "type": "integer", "description": "Line number in the script." },
+                    { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script." }
+                ],
+                "description": "Location in the source code."
+            },
+            {
+                "id": "CallFrame",
+                "type": "object",
+                "properties": [
+                    { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." },
+                    { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." },
+                    { "name": "location", "$ref": "Location", "description": "Location in the source code." },
+                    { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." },
+                    { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." }
+                ],
+                "description": "JavaScript call frame. Array of call frames form the call stack."
+            },
+            {
+                "id": "Scope",
+                "type": "object",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch"], "description": "Scope type." },
+                    { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }
+                ],
+                "description": "Scope description."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables debugger for given page."
+            },
+            {
+                "name": "disable",
+                "description": "Disables debugger for given page."
+            },
+            {
+                "name": "setBreakpointsActive",
+                "parameters": [
+                    { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." }
+                ],
+                "description": "Activates / deactivates all breakpoints on the page."
+            },
+            {
+                "name": "setBreakpointByUrl",
+                "parameters": [
+                    { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." },
+                    { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." },
+                    { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." },
+                    { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." },
+                    { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
+                ],
+                "returns": [
+                    { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
+                    { "name": "locations", "optional": true, "type": "array", "items": { "$ref": "Location"}, "description": "List of the locations this breakpoint resolved into upon addition." }
+                ],
+                "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads."
+            },
+            {
+                "name": "setBreakpoint",
+                "parameters": [
+                    { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." },
+                    { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
+                ],
+                "returns": [
+                    { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
+                    { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." }
+                ],
+                "description": "Sets JavaScript breakpoint at a given location."
+            },
+            {
+                "name": "removeBreakpoint",
+                "parameters": [
+                    { "name": "breakpointId", "$ref": "BreakpointId" }
+                ],
+                "description": "Removes JavaScript breakpoint."
+            },
+            {
+                "name": "continueToLocation",
+                "parameters": [
+                    { "name": "location", "$ref": "Location", "description": "Location to continue to." }
+                ],
+                "description": "Continues execution until specific location is reached."
+            },
+            {
+                "name": "stepOver",
+                "description": "Steps over the statement."
+            },
+            {
+                "name": "stepInto",
+                "description": "Steps into the function call."
+            },
+            {
+                "name": "stepOut",
+                "description": "Steps out of the function call."
+            },
+            {
+                "name": "pause",
+                "description": "Stops on the next JavaScript statement."
+            },
+            {
+                "name": "resume",
+                "description": "Resumes JavaScript execution."
+            },
+            {
+                "name": "setScriptSource",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to edit." },
+                    { "name": "scriptSource", "type": "string", "description": "New content of the script." },
+                    { "name": "preview", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", "hidden": true }
+                ],
+                "returns": [
+                    { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame"}, "description": "New stack trace in case editing has happened while VM was stopped." },
+                    { "name": "result", "type": "object", "optional": true, "description": "VM-specific description of the changes applied.", "hidden": true }
+                ],
+                "description": "Edits JavaScript source live."
+            },
+            {
+                "name": "getScriptSource",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to get source for." }
+                ],
+                "returns": [
+                    { "name": "scriptSource", "type": "string", "description": "Script source." }
+                ],
+                "description": "Returns source for the script with given id."
+            },
+            {
+                "name": "setPauseOnExceptions",
+                "parameters": [
+                    { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." }
+                ],
+                "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>."
+            },
+            {
+                "name": "evaluateOnCallFrame",
+                "parameters": [
+                    { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." },
+                    { "name": "expression", "type": "string", "description": "Expression to evaluate." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." },
+                    { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true },
+                    { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ],
+                "description": "Evaluates expression on a given call frame."
+            }
+        ],
+        "events": [
+            {
+                "name": "debuggerWasEnabled",
+                "description": "Fired when debugger gets enabled (deprecated).",
+                "hidden": true
+            },
+            {
+                "name": "debuggerWasDisabled",
+                "description": "Fired when debugger gets disabled (deprecated).",
+                "hidden": true
+            },
+            {
+                "name": "scriptParsed",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Identifier of the script parsed." },
+                    { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." },
+                    { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." },
+                    { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." },
+                    { "name": "endLine", "type": "integer", "description": "Last line of the script." },
+                    { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." },
+                    { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }
+                ],
+                "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger."
+            },
+            {
+                "name": "scriptFailedToParse",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL of the script that failed to parse." },
+                    { "name": "scriptSource", "type": "string", "description": "Source text of the script that failed to parse." },
+                    { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource." },
+                    { "name": "errorLine", "type": "integer", "description": "Line with error." },
+                    { "name": "errorMessage", "type": "string", "description": "Parse error message." }
+                ],
+                "description": "Fired when virtual machine fails to parse the script."
+            },
+            {
+                "name": "breakpointResolved",
+                "parameters": [
+                    { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." },
+                    { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." }
+                ],
+                "description": "Fired when breakpoint is resolved to an actual script and location."
+            },
+            {
+                "name": "paused",
+                "parameters": [
+                    { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." },
+                    { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "other" ], "description": "Pause reason." },
+                    { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }
+                ],
+                "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria."
+            },
+            {
+                "name": "resumed",
+                "description": "Fired when the virtual machine resumed execution."
+            }
+        ]
+    },
+    {
+        "domain": "DOMDebugger",
+        "description": "DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript execution will stop on these operations as if there was a regular breakpoint set.",
+        "types": [
+            {
+                "id": "DOMBreakpointType",
+                "type": "string",
+                "enum": ["subtree-modified", "attribute-modified", "node-removed"],
+                "description": "DOM breakpoint type."
+            }
+        ],
+        "commands": [
+            {
+                "name": "setDOMBreakpoint",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to set breakpoint on." },
+                    { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the operation to stop upon." }
+                ],
+                "description": "Sets breakpoint on particular operation with DOM."
+            },
+            {
+                "name": "removeDOMBreakpoint",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to remove breakpoint from." },
+                    { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the breakpoint to remove." }
+                ],
+                "description": "Removes DOM breakpoint that was set using <code>setDOMBreakpoint</code>."
+            },
+            {
+                "name": "setEventListenerBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "Event name to stop on (any DOM event will do)." }
+                ],
+                "description": "Sets breakpoint on particular DOM event."
+            },
+            {
+                "name": "removeEventListenerBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "Event name." }
+                ],
+                "description": "Removes breakpoint on particular DOM event."
+            },
+            {
+                "name": "setXHRBreakpoint",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "Resource URL substring. All XHRs having this substring in the URL will get stopped upon." }
+                ],
+                "description": "Sets breakpoint on XMLHttpRequest."
+            },
+            {
+                "name": "removeXHRBreakpoint",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "Resource URL substring." }
+                ],
+                "description": "Removes breakpoint from XMLHttpRequest."
+            }
+        ]
+    },
+    {
+        "domain": "Profiler",
+        "hidden": true,
+        "types": [
+            {
+                "id": "Profile",
+                "type": "object",
+                "description": "Profile."
+            },
+            {
+                "id": "ProfileHeader",
+                "type": "object",
+                "description": "Profile header."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable"
+            },
+            {
+                "name": "disable"
+            },
+            {
+                "name": "isEnabled",
+                "returns": [
+                    { "name": "state", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "start"
+            },
+            {
+                "name": "stop"
+            },
+            {
+                "name": "getProfileHeaders",
+                "returns": [
+                    { "name": "headers", "type": "array", "items": { "$ref": "ProfileHeader"} }
+                ]
+            },
+            {
+                "name": "getProfile",
+                "parameters": [
+                    { "name": "type", "type": "string" },
+                    { "name": "uid", "type": "integer" }
+                ],
+                "returns": [
+                    { "name": "profile", "$ref": "Profile" }
+                ]
+            },
+            {
+                "name": "removeProfile",
+                "parameters": [
+                    { "name": "type", "type": "string" },
+                    { "name": "uid", "type": "integer" }
+                ]
+            },
+            {
+                "name": "clearProfiles"
+            },
+            {
+                "name": "takeHeapSnapshot"
+            },
+            {
+                "name": "collectGarbage"
+            }
+        ],
+        "events": [
+            {
+                "name": "profilerWasEnabled"
+            },
+            {
+                "name": "profilerWasDisabled"
+            },
+            {
+                "name": "addProfileHeader",
+                "parameters": [
+                    { "name": "header", "$ref": "ProfileHeader" }
+                ]
+            },
+            {
+                "name": "addHeapSnapshotChunk",
+                "parameters": [
+                    { "name": "uid", "type": "integer" },
+                    { "name": "chunk", "type": "string" }
+                ]
+            },
+            {
+                "name": "finishHeapSnapshot",
+                "parameters": [
+                    { "name": "uid", "type": "integer" }
+                ]
+            },
+            {
+                "name": "setRecordingProfile",
+                "parameters": [
+                    { "name": "isProfiling", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "resetProfiles"
+            },
+            {
+                "name": "reportHeapSnapshotProgress",
+                "parameters": [
+                    { "name": "done", "type": "integer" },
+                    { "name": "total", "type": "integer" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "Worker",
+        "hidden": true,
+        "types": [],
+        "commands": [
+            {
+                "name": "setWorkerInspectionEnabled",
+                "parameters": [
+                    { "name": "value", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "sendMessageToWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" },
+                    { "name": "message", "type": "object" }
+                ]
+            },
+            {
+                "name": "connectToWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" }
+                ]
+            },
+            {
+                "name": "disconnectFromWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" }
+                ]
+            },
+            {
+                "name": "setAutoconnectToWorkers",
+                "parameters": [
+                    { "name": "value", "type": "boolean" }
+                ]
+            }
+
+        ],
+        "events": [
+            {
+                "name": "workerCreated",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" },
+                    { "name": "url", "type": "string" },
+                    { "name": "inspectorConnected", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "workerTerminated",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" }
+                ]
+            },
+            {
+                "name": "dispatchMessageFromWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" },
+                    { "name": "message", "type": "object" }
+                ]
+            }
+        ]
+    }]
+}
diff --git a/Source/devtools/Inspector-1.0.json b/Source/devtools/Inspector-1.0.json
new file mode 100644
index 0000000..e19e7e0
--- /dev/null
+++ b/Source/devtools/Inspector-1.0.json
@@ -0,0 +1,2503 @@
+{
+    "domains": [{
+        "domain": "Inspector",
+        "hidden": true,
+        "types": [],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables inspector domain notifications."
+            },
+            {
+                "name": "disable",
+                "description": "Disables inspector domain notifications."
+            }
+        ],
+        "events": [
+            {
+                "name": "evaluateForTestInFrontend",
+                "parameters": [
+                    { "name": "testCallId", "type": "integer" },
+                    { "name": "script", "type": "string" }
+                ]
+            },
+            {
+                "name": "inspect",
+                "parameters": [
+                    { "name": "object", "$ref": "Runtime.RemoteObject" },
+                    { "name": "hints", "type": "object" }
+                ]
+            },
+            {
+                "name": "didCreateWorker",
+                "parameters": [
+                    { "name": "id", "type": "integer" },
+                    { "name": "url", "type": "string" },
+                    { "name": "isShared", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "didDestroyWorker",
+                "parameters": [
+                    { "name": "id", "type": "integer" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "Memory",
+        "hidden": true,
+        "types": [
+            {
+                "id": "NodeCount",
+                "type": "object",
+                "properties": [
+                    { "name": "nodeName", "type": "string" },
+                    { "name": "count", "type": "integer" }
+                ],
+                "description": "Number of nodes with given name."
+            },
+            {
+                "id": "ListenerCount",
+                "type": "object",
+                "properties": [
+                    { "name": "type", "type": "string" },
+                    { "name": "count", "type": "integer" }
+                ],
+                "description": "Number of JS event listeners by event type."
+            },
+            {
+                "id": "StringStatistics",
+                "type": "object",
+                "properties": [
+                    { "name": "dom", "type": "integer" },
+                    { "name": "js", "type": "integer" },
+                    { "name": "shared", "type": "integer" }
+                ],
+                "description": "Character data statistics for the page."
+            },
+            {
+                "id": "DOMGroup",
+                "type": "object",
+                "properties": [
+                    { "name": "size", "type": "integer" },
+                    { "name": "title", "type": "string" },
+                    { "name": "documentURI", "type": "string", "optional": true },
+                    { "name": "nodeCount", "type": "array", "items": { "$ref": "NodeCount" }},
+                    { "name": "listenerCount", "type": "array", "items": { "$ref": "ListenerCount" }}
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "getDOMNodeCount",
+                "returns": [
+                    { "name": "domGroups", "type": "array", "items": { "$ref": "DOMGroup" }},
+                    { "name": "strings", "$ref": "StringStatistics" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "Page",
+        "description": "Actions and events related to the inspected page belong to the page domain.",
+        "types": [
+            {
+                "id": "ResourceType",
+                "type": "string",
+                "enum": ["Document", "Stylesheet", "Image", "Font", "Script", "XHR", "WebSocket", "Other"],
+                "description": "Resource type as it was perceived by the rendering engine."
+            },
+            {
+                "id": "Frame",
+                "type": "object",
+                "description": "Information about the Frame on the page.",
+                "properties": [
+                    { "name": "id", "type": "string", "description": "Frame unique identifier." },
+                    { "name": "parentId", "type": "string", "optional": true, "description": "Parent frame identifier." },
+                    { "name": "loaderId", "$ref": "Network.LoaderId", "description": "Identifier of the loader associated with this frame." },
+                    { "name": "name", "type": "string", "optional": true, "description": "Frame's name as specified in the tag." },
+                    { "name": "url", "type": "string", "description": "Frame document's URL." },
+                    { "name": "securityOrigin", "type": "string", "optional": true, "description": "Frame document's security origin." },
+                    { "name": "mimeType", "type": "string", "description": "Frame document's mimeType as determined by the browser." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "FrameResourceTree",
+                "type": "object",
+                "description": "Information about the Frame hierarchy along with their cached resources.",
+                "properties": [
+                    { "name": "frame", "$ref": "Frame", "description": "Frame information for this tree item." },
+                    { "name": "childFrames", "type": "array", "optional": true, "items": { "$ref": "FrameResourceTree" }, "description": "Child frames." },
+                    { "name": "resources", "type": "array",
+                        "items": {
+                            "type": "object",
+                            "properties": [
+                                { "name": "url", "type": "string", "description": "Resource URL." },
+                                { "name": "type", "$ref": "ResourceType", "description": "Type of this resource." },
+                                { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }
+                            ]
+                        },
+                        "description": "Information about frame resources."
+                    }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "SearchMatch",
+                "type": "object",
+                "description": "Search match for resource.",
+                "properties": [
+                    { "name": "lineNumber", "type": "number", "description": "Line number in resource content." },
+                    { "name": "lineContent", "type": "string", "description": "Line with match content." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "SearchResult",
+                "type": "object",
+                "description": "Search result for resource.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Resource URL." },
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Resource frame id." },
+                    { "name": "matchesCount", "type": "number", "description": "Number of matches in the resource content." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "Cookie",
+                "type": "object",
+                "description": "Cookie object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Cookie name." },
+                    { "name": "value", "type": "string", "description": "Cookie value." },
+                    { "name": "domain", "type": "string", "description": "Cookie domain." },
+                    { "name": "path", "type": "string", "description": "Cookie path." },
+                    { "name": "expires", "type": "integer", "description": "Cookie expires." },
+                    { "name": "size", "type": "integer", "description": "Cookie size." },
+                    { "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." },
+                    { "name": "secure", "type": "boolean", "description": "True if cookie is secure." },
+                    { "name": "session", "type": "boolean", "description": "True in case of session cookie." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "ScriptIdentifier",
+                "type": "string",
+                "description": "Unique script identifier.",
+                "hidden": true
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables page domain notifications."
+            },
+            {
+                "name": "disable",
+                "description": "Disables page domain notifications."
+            },
+            {
+                "name": "addScriptToEvaluateOnLoad",
+                "parameters": [
+                    { "name": "scriptSource", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "identifier", "$ref": "ScriptIdentifier", "description": "Identifier of the added script." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "removeScriptToEvaluateOnLoad",
+                "parameters": [
+                    { "name": "identifier", "$ref": "ScriptIdentifier" }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "reload",
+                "parameters": [
+                    { "name": "ignoreCache", "type": "boolean", "optional": true, "description": "If true, browser cache is ignored (as if the user pressed Shift+refresh)." },
+                    { "name": "scriptToEvaluateOnLoad", "type": "string", "optional": true, "description": "If set, the script will be injected into all frames of the inspected page after reload." }
+                ],
+                "description": "Reloads given page optionally ignoring the cache."
+            },
+            {
+                "name": "navigate",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL to navigate the page to." }
+                ],
+                "description": "Navigates current page to the given URL."
+            },
+            {
+                "name": "getCookies",
+                "returns": [
+                    { "name": "cookies", "type": "array", "items": { "$ref": "Cookie"}, "description": "Array of cookie objects." },
+                    { "name": "cookiesString", "type": "string", "description": "document.cookie string representation of the cookies." }
+                ],
+                "description": "Returns all browser cookies. Depending on the backend support, will either return detailed cookie information in the <code>cookie</code> field or string cookie representation using <code>cookieString</code>.",
+                "hidden": true
+            },
+            {
+                "name": "deleteCookie",
+                "parameters": [
+                    { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." },
+                    { "name": "domain", "type": "string", "description": "Domain of the cookie to remove." }
+                ],
+                "description": "Deletes browser cookie with given name for the given domain.",
+                "hidden": true
+            },
+            {
+                "name": "getResourceTree",
+                "description": "Returns present frame / resource tree structure.",
+                "returns": [
+                    { "name": "frameTree", "$ref": "FrameResourceTree", "description": "Present frame / resource tree structure." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "getResourceContent",
+                "description": "Returns content of the given resource.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame id to get resource for." },
+                    { "name": "url", "type": "string", "description": "URL of the resource to get content for." }
+                ],
+                "returns": [
+                    { "name": "content", "type": "string", "description": "Resource content." },
+                    { "name": "base64Encoded", "type": "boolean", "description": "True, if content was served as base64." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "searchInResource",
+                "description": "Searches for given string in resource content.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame id for resource to search in." },
+                    { "name": "url", "type": "string", "description": "URL of the resource to search in." },
+                    { "name": "query", "type": "string", "description": "String to search for."  },
+                    { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." },
+                    { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "searchInResources",
+                "description": "Searches for given string in frame / resource tree structure.",
+                "parameters": [
+                    { "name": "text", "type": "string", "description": "String to search for."  },
+                    { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." },
+                    { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "SearchResult" }, "description": "List of search results." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setDocumentContent",
+                "description": "Sets given markup as the document's HTML.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame id to set HTML for." },
+                    { "name": "html", "type": "string", "description": "HTML content to set."  }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setScreenSizeOverride",
+                "description": "Overrides the values of window.screen.width, window.screen.height, window.innerWidth, window.innerHeight, and \"device-width\"/\"device-height\"-related CSS media query results",
+                "parameters": [
+                    { "name": "width", "type": "integer", "description": "Overriding width value in pixels (minimum 0, maximum 10000000). 0 disables the override." },
+                    { "name": "height", "type": "integer", "description": "Overriding height value in pixels (minimum 0, maximum 10000000). 0 disables the override." }
+                ],
+                "hidden": true
+            }
+        ],
+        "events": [
+            {
+                "name": "domContentEventFired",
+                "parameters": [
+                    { "name": "timestamp", "type": "number" }
+                ]
+            },
+            {
+                "name": "loadEventFired",
+                "parameters": [
+                    { "name": "timestamp", "type": "number" }
+                ]
+            },
+            {
+                "name": "frameNavigated",
+                "description": "Fired once navigation of the frame has completed. Frame is now associated with the new loader.",
+                "parameters": [
+                    { "name": "frame", "$ref": "Frame", "description": "Frame object." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "frameDetached",
+                "description": "Fired when frame has been detached from its parent.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Id of the frame that has been detached." }
+                ],
+                "hidden": true
+            }
+        ]
+    },
+    {
+        "domain": "Runtime",
+        "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.",
+        "types": [
+            {
+                "id": "RemoteObjectId",
+                "type": "string",
+                "description": "Unique object identifier."
+            },
+            {
+                "id": "RemoteObject",
+                "type": "object",
+                "description": "Mirror object referencing original JavaScript object.",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type." },
+                    { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." },
+                    { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." },
+                    { "name": "value", "type": "any", "optional": true, "description": "Remote object value (in case of primitive values or JSON values if it was requested)." },
+                    { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." },
+                    { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }
+                ]
+            },
+            {
+                "id": "PropertyDescriptor",
+                "type": "object",
+                "description": "Object property descriptor.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Property name." },
+                    { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." },
+                    { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." },
+                    { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." },
+                    { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." },
+                    { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." },
+                    { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ]
+            },
+            {
+                "id": "CallArgument",
+                "type": "object",
+                "description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.",
+                "properties": [
+                    { "name": "value", "type": "any", "optional": true, "description": "Primitive value." },
+                    { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "evaluate",
+                "parameters": [
+                    { "name": "expression", "type": "string", "description": "Expression to evaluate." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." },
+                    { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true },
+                    { "name": "doNotPauseOnExceptions", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions. Overrides setPauseOnException state.", "hidden": true },
+                    { "name": "frameId", "$ref": "Network.FrameId", "optional": true, "description": "Specifies in which frame to perform evaluation.", "hidden": true },
+                    { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ],
+                "description": "Evaluates expression on global object."
+            },
+            {
+                "name": "callFunctionOn",
+                "parameters": [
+                    { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." },
+                    { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." },
+                    { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." },
+                    { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "RemoteObject", "description": "Call result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ],
+                "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object."
+            },
+            {
+                "name": "getProperties",
+                "parameters": [
+                    { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." },
+                    { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor"}, "description": "Object properties." }
+                ],
+                "description": "Returns properties of a given object. Object group of the result is inherited from the target object."
+            },
+            {
+                "name": "releaseObject",
+                "parameters": [
+                    { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." }
+                ],
+                "description": "Releases remote object with given id."
+            },
+            {
+                "name": "releaseObjectGroup",
+                "parameters": [
+                    { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." }
+                ],
+                "description": "Releases all remote objects that belong to a given group."
+            },
+            {
+                "name": "run",
+                "hidden": true,
+                "description": "Tells inspected instance(worker or page) that it can run in case it was started paused."
+            }
+        ]
+    },
+    {
+        "domain": "Console",
+        "description": "Console domain defines methods and events for interaction with the JavaScript console. Console collects messages created by means of the <a href='http://getfirebug.com/wiki/index.php/Console_API'>JavaScript Console API</a>. One needs to enable this domain using <code>enable</code> command in order to start receiving the console messages. Browser collects messages issued while console domain is not enabled as well and reports them using <code>messageAdded</code> notification upon enabling.",
+        "types": [
+            {
+                "id": "ConsoleMessage",
+                "type": "object",
+                "description": "Console message.",
+                "properties": [
+                    { "name": "source", "type": "string", "enum": ["html", "wml", "xml", "javascript", "network", "console-api", "other"], "description": "Message source." },
+                    { "name": "level", "type": "string", "enum": ["tip", "log", "warning", "error", "debug"], "description": "Message severity." },
+                    { "name": "text", "type": "string", "description": "Message text." },
+                    { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "trace", "startGroup", "startGroupCollapsed", "endGroup", "assert"], "description": "Console message type." },
+                    { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." },
+                    { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." },
+                    { "name": "repeatCount", "type": "integer", "optional": true, "description": "Repeat count for repeated messages." },
+                    { "name": "parameters", "type": "array", "items": { "$ref": "Runtime.RemoteObject" }, "optional": true, "description": "Message parameters in case of the formatted message." },
+                    { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." },
+                    { "name": "networkRequestId", "$ref": "Network.RequestId", "optional": true, "description": "Identifier of the network request associated with this message." }
+                ]
+            },
+            {
+                "id": "CallFrame",
+                "type": "object",
+                "description": "Stack entry for console errors and assertions.",
+                "properties": [
+                    { "name": "functionName", "type": "string", "description": "JavaScript function name." },
+                    { "name": "url", "type": "string", "description": "JavaScript script name or url." },
+                    { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number." },
+                    { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number." }
+                ]
+            },
+            {
+                "id": "StackTrace",
+                "type": "array",
+                "items": { "$ref": "CallFrame" },
+                "description": "Call frames for assertions or error messages."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification." 
+            },
+            {
+                "name": "disable",
+                "description": "Disables console domain, prevents further console messages from being reported to the client." 
+            },
+            {
+                "name": "clearMessages",
+                "description": "Clears console messages collected in the browser." 
+            },
+            {
+                "name": "setMonitoringXHREnabled",
+                "parameters": [
+                    { "name": "enabled", "type": "boolean", "description": "Monitoring enabled state." }
+                ],
+                "description": "Toggles monitoring of XMLHttpRequest. If <code>true</code>, console will receive messages upon each XHR issued.", 
+                "hidden": true
+            },
+            {
+                "name": "addInspectedNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "DOM node id to be accessible by means of $x command line API." }
+                ],
+                "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions).",
+                "hidden": true
+            }
+        ],
+        "events": [
+            {
+                "name": "messageAdded",
+                "parameters": [
+                    { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." }
+                ],
+                "description": "Issued when new console message is added."
+            },
+            {
+                "name": "messageRepeatCountUpdated",
+                "parameters": [
+                    { "name": "count", "type": "integer", "description": "New repeat count value." }
+                ],
+                "description": "Issued when subsequent message(s) are equal to the previous one(s)."
+            },
+            {
+                "name": "messagesCleared",
+                "description": "Issued when console is cleared. This happens either upon <code>clearMessages</code> command or after page navigation."
+            }
+        ]
+    },
+    {
+        "domain": "Network",
+        "description": "Network domain allows tracking network activities of the page. It exposes information about http, file, data and other requests and responses, their headers, bodies, timing, etc.",
+        "types": [
+            {
+                "id": "LoaderId",
+                "type": "string",
+                "description": "Unique loader identifier."
+            },
+            {
+                "id": "FrameId",
+                "type": "string",
+                "description": "Unique frame identifier.",
+                "hidden": true
+            },
+            {
+                "id": "RequestId",
+                "type": "string",
+                "description": "Unique request identifier."
+            },
+            {
+                "id": "Timestamp",
+                "type": "number",
+                "description": "Number of seconds since epoch."
+            },
+            {
+                "id": "Headers",
+                "type": "object",
+                "description": "Request / response headers as keys / values of JSON object."
+            },
+            {
+                "id": "ResourceTiming",
+                "type": "object",
+                "description": "Timing information for the request.",
+                "properties": [
+                    { "name": "requestTime", "type": "number", "description": "Timing's requestTime is a baseline in seconds, while the other numbers are ticks in milliseconds relatively to this requestTime." },
+                    { "name": "proxyStart", "type": "number", "description": "Started resolving proxy." },
+                    { "name": "proxyEnd", "type": "number", "description": "Finished resolving proxy." },
+                    { "name": "dnsStart", "type": "number", "description": "Started DNS address resolve." },
+                    { "name": "dnsEnd", "type": "number", "description": "Finished DNS address resolve." },
+                    { "name": "connectStart", "type": "number", "description": "Started connecting to the remote host." },
+                    { "name": "connectEnd", "type": "number", "description": "Connected to the remote host." },
+                    { "name": "sslStart", "type": "number", "description": "Started SSL handshake." },
+                    { "name": "sslEnd", "type": "number", "description": "Finished SSL handshake." },
+                    { "name": "sendStart", "type": "number", "description": "Started sending request." },
+                    { "name": "sendEnd", "type": "number", "description": "Finished sending request." },
+                    { "name": "receiveHeadersEnd", "type": "number", "description": "Finished receiving response headers." }
+                ]
+            },
+            {
+                "id": "Request",
+                "type": "object",
+                "description": "HTTP request data.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Request URL." },
+                    { "name": "method", "type": "string", "description": "HTTP request method." },
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP request headers." },
+                    { "name": "postData", "type": "string", "optional": true, "description": "HTTP POST request data." }
+                ]
+            },
+            {
+                "id": "Response",
+                "type": "object",
+                "description": "HTTP response data.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Response URL." },
+                    { "name": "status", "type": "number", "description": "HTTP response status code." },
+                    { "name": "statusText", "type": "string", "description": "HTTP response status text." },
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." },
+                    { "name": "headersText", "type": "string", "optional": true, "description": "HTTP response headers text." },
+                    { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." },
+                    { "name": "requestHeaders", "$ref": "Headers", "optional": true, "description": "Refined HTTP request headers that were actually transmitted over the network." },
+                    { "name": "requestHeadersText", "type": "string", "optional": true, "description": "HTTP request headers text." },
+                    { "name": "connectionReused", "type": "boolean", "description": "Specifies whether physical connection was actually reused for this request." },
+                    { "name": "connectionId", "type": "number", "description": "Physical connection id that was actually used for this request." },
+                    { "name": "fromDiskCache", "type": "boolean", "optional": true, "description": "Specifies that the request was served from the disk cache." },
+                    { "name": "timing", "$ref": "ResourceTiming", "optional": true, "description": "Timing information for the given request." }
+                ]
+            },
+            {
+                "id": "WebSocketRequest",
+                "type": "object",
+                "description": "WebSocket request data.",
+                "hidden": true,
+                "properties": [
+                    { "name": "requestKey3", "type": "string", "description": "HTTP response status text." },
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." }
+                ]
+            },
+            {
+                "id": "WebSocketResponse",
+                "type": "object",
+                "description": "WebSocket response data.",
+                "hidden": true,
+                "properties": [
+                    { "name": "status", "type": "number", "description": "HTTP response status code." },
+                    { "name": "statusText", "type": "string", "description": "HTTP response status text." },
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." },
+                    { "name": "challengeResponse", "type": "string", "description": "Challenge response." }
+                ]
+            },
+            {
+                "id": "CachedResource",
+                "type": "object",
+                "description": "Information about the cached resource.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Resource URL." },
+                    { "name": "type", "$ref": "Page.ResourceType", "description": "Type of this resource." },
+                    { "name": "response", "$ref": "Response", "optional": true, "description": "Cached response data." },
+                    { "name": "bodySize", "type": "number", "description": "Cached response body size." }
+                ]
+            },
+            {
+                "id": "Initiator",
+                "type": "object",
+                "description": "Information about the request initiator.",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["parser", "script", "other"], "description": "Type of this initiator." },
+                    { "name": "stackTrace", "$ref": "Console.StackTrace", "optional": true, "description": "Initiator JavaScript stack trace, set for Script only." },
+                    { "name": "url", "type": "string", "optional": true, "description": "Initiator URL, set for Parser type only." },
+                    { "name": "lineNumber", "type": "number", "optional": true, "description": "Initiator line number, set for Parser type only." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables network tracking, network events will now be delivered to the client."
+            },
+            {
+                "name": "disable",
+                "description": "Disables network tracking, prevents network events from being sent to the client."
+            },
+            {
+                "name": "setUserAgentOverride",
+                "description": "Allows overriding user agent with the given string.",
+                "parameters": [
+                    { "name": "userAgent", "type": "string", "description": "User agent to use." }
+                ]
+            },
+            {
+                "name": "setExtraHTTPHeaders",
+                "description": "Specifies whether to always send extra HTTP headers with the requests from this page.",
+                "parameters": [
+                    { "name": "headers", "$ref": "Headers", "description": "Map with extra HTTP headers." }
+                ]
+            },
+            {
+                "name": "getResponseBody",
+                "description": "Returns content served for the given request.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Identifier of the network request to get content for." }
+                ],
+                "returns": [
+                    { "name": "body", "type": "string", "description": "Response body." },
+                    { "name": "base64Encoded", "type": "boolean", "description": "True, if content was sent as base64." }
+                ]
+            },
+            {
+                "name": "canClearBrowserCache",
+                "description": "Tells whether clearing browser cache is supported.",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "True if browser cache can be cleared." }
+                ]
+            },
+            {
+                "name": "clearBrowserCache",
+                "description": "Clears browser cache."
+            },
+            {
+                "name": "canClearBrowserCookies",
+                "description": "Tells whether clearing browser cookies is supported.",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "True if browser cookies can be cleared." }
+                ]
+            },
+            {
+                "name": "clearBrowserCookies",
+                "description": "Clears browser cookies."
+            },
+            {
+                "name": "setCacheDisabled",
+                "parameters": [
+                    { "name": "cacheDisabled", "type": "boolean", "description": "Cache disabled state." }
+                ],
+                "description": "Toggles ignoring cache for each request. If <code>true</code>, cache will not be used." 
+            }
+        ],
+        "events": [
+            {
+                "name": "requestWillBeSent",
+                "description": "Fired when page is about to send HTTP request.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "frameId", "$ref": "FrameId", "description": "Frame identifier.", "hidden": true },
+                    { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." },
+                    { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." },
+                    { "name": "request", "$ref": "Request", "description": "Request data." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." },
+                    { "name": "stackTrace", "$ref": "Console.StackTrace", "optional": true, "description": "JavaScript stack trace upon issuing this request." },
+                    { "name": "redirectResponse", "optional": true, "$ref": "Response", "description": "Redirect response data." }
+                ]
+            },
+            {
+                "name": "requestServedFromCache",
+                "description": "Fired if request ended up loading from cache.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }
+                ]
+            },
+            {
+                "name": "responseReceived",
+                "description": "Fired when HTTP response is available.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "frameId", "$ref": "FrameId", "description": "Frame identifier.", "hidden": true },
+                    { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "type", "$ref": "Page.ResourceType", "description": "Resource type." },
+                    { "name": "response", "$ref": "Response", "description": "Response data." }
+                ]
+            },
+            {
+                "name": "dataReceived",
+                "description": "Fired when data chunk was received over the network.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "dataLength", "type": "integer", "description": "Data chunk length." },
+                    { "name": "encodedDataLength", "type": "integer", "description": "Actual bytes received (might be less than dataLength for compressed encodings)." }
+                ]
+            },
+            {
+                "name": "loadingFinished",
+                "description": "Fired when HTTP request has finished loading.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }
+                ]
+            },
+            {
+                "name": "loadingFailed",
+                "description": "Fired when HTTP request has failed to load.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "errorText", "type": "string", "description": "User friendly error message." },
+                    { "name": "canceled", "type": "boolean", "optional": true, "description": "True if loading was canceled." }
+                ]
+            },
+            {
+                "name": "requestServedFromMemoryCache",
+                "description": "Fired when HTTP request has been served from memory cache.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "frameId", "$ref": "FrameId", "description": "Frame identifier.", "hidden": true },
+                    { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." },
+                    { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." },
+                    { "name": "resource", "$ref": "CachedResource", "description": "Cached resource data." }
+                ]
+            },
+            {
+                "name": "webSocketWillSendHandshakeRequest",
+                "description": "Fired when WebSocket is about to initiate handshake.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "request", "$ref": "WebSocketRequest", "description": "WebSocket request data." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketHandshakeResponseReceived",
+                "description": "Fired when WebSocket handshake response becomes available.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "response", "$ref": "WebSocketResponse", "description": "WebSocket response data." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketCreated",
+                "description": "Fired upon WebSocket creation.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "url", "type": "string", "description": "WebSocket request URL." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketClosed",
+                "description": "Fired when WebSocket is closed.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }
+                ],
+                "hidden": true
+            }
+        ]
+    },
+    {
+        "domain": "Database",
+        "hidden": true,
+        "types": [
+            {
+                "id": "Database",
+                "type": "object",
+                "description": "Database object.",
+                "hidden": true,
+                "properties": [
+                    { "name": "id", "type": "string", "description": "Database ID." },
+                    { "name": "domain", "type": "string", "description": "Database domain." },
+                    { "name": "name", "type": "string", "description": "Database name." },
+                    { "name": "version", "type": "string", "description": "Database version." }
+                ]
+            },
+            {
+                "id": "Error",
+                "type": "object",
+                "description": "Database error."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables database tracking, database events will now be delivered to the client."
+            },
+            {
+                "name": "disable",
+                "description": "Disables database tracking, prevents database events from being sent to the client."
+            },
+            {
+                "name": "getDatabaseTableNames",
+                "parameters": [
+                    { "name": "databaseId", "type": "integer" }
+                ],
+                "returns": [
+                    { "name": "tableNames", "type": "array", "items": { "type": "string" } }
+                ]
+            },
+            {
+                "name": "executeSQL",
+                "parameters": [
+                    { "name": "databaseId", "type": "integer" },
+                    { "name": "query", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "success", "type": "boolean" },
+                    { "name": "transactionId", "type": "integer" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "addDatabase",
+                "parameters": [
+                    { "name": "database", "$ref": "Database" }
+                ]
+            },
+            {
+                "name": "sqlTransactionSucceeded",
+                "parameters": [
+                    { "name": "transactionId", "type": "integer" },
+                    { "name": "columnNames", "type": "array", "items": { "type": "string" } },
+                    { "name": "values", "type": "array", "items": { "type": "any" }}
+                ]
+            },
+            {
+                "name": "sqlTransactionFailed",
+                "parameters": [
+                    { "name": "transactionId", "type": "integer" },
+                    { "name": "sqlError", "$ref": "Error" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "IndexedDB",
+        "hidden": true,
+        "types": [
+            {
+                "id": "SecurityOriginWithDatabaseNames",
+                "type": "object",
+                "description": "Security origin with database names.",
+                "properties": [
+                    { "name": "securityOrigin", "type": "string", "description": "Security origin." },
+                    { "name": "databaseNames", "type": "array", "items": { "type": "string" }, "description": "Database names for this origin." }
+                ]
+            },
+            {
+                "id": "DatabaseWithObjectStores",
+                "type": "object",
+                "description": "Database with an array of object stores.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Database name." },
+                    { "name": "version", "type": "string", "description": "Database version." },
+                    { "name": "objectStores", "type": "array", "items": { "$ref": "ObjectStore" }, "description": "Object stores in this database." }
+                ]
+            },
+            {
+                "id": "ObjectStore",
+                "type": "object",
+                "description": "Object store.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Object store name." },
+                    { "name": "keyPath", "type": "string", "description": "Object store key path." },
+                    { "name": "indexes", "type": "array", "items": { "$ref": "ObjectStoreIndex" }, "description": "Indexes in this object store." }
+                ]
+            },
+            {
+                "id": "ObjectStoreIndex",
+                "type": "object",
+                "description": "Object store index.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Index name." },
+                    { "name": "keyPath", "type": "string", "description": "Index key path." },
+                    { "name": "unique", "type": "boolean", "description": "If true, index is unique." },
+                    { "name": "multiEntry", "type": "boolean", "description": "If true, index allows multiple entries for a key." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables events from backend."
+            },
+            {
+                "name": "disable",
+                "description": "Disables events from backend."
+            },
+            {
+                "name": "requestDatabaseNamesForFrame",
+                "parameters": [
+                    { "name": "requestId", "type": "integer", "description": "Request id." },
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame id." }
+                ],
+                "description": "Requests database names for given frame's security origin."
+            },
+            {
+                "name": "requestDatabase",
+                "parameters": [
+                    { "name": "requestId", "type": "integer", "description": "Request id." },
+                    { "name": "frameId", "$ref": "Network.FrameId" },
+                    { "name": "databaseName", "type": "string" }
+                ],
+                "description": "Requests database with given name in given frame."
+            }
+        ],
+        "events": [
+            {
+                "name": "databaseNamesLoaded",
+                "parameters": [
+                    { "name": "requestId", "type": "number", "description": "Request id." },
+                    { "name": "securityOriginWithDatabaseNames", "$ref": "SecurityOriginWithDatabaseNames", "description": "Frame with database names." }
+                ]
+            },
+            {
+                "name": "databaseLoaded",
+                "parameters": [
+                    { "name": "requestId", "type": "integer", "description": "Request id." },
+                    { "name": "databaseWithObjectStores", "$ref": "DatabaseWithObjectStores", "description": "Database with an array of object stores." }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "DOMStorage",
+        "hidden": true,
+        "types": [
+            {
+                "id": "Entry",
+                "type": "object",
+                "description": "DOM Storage entry.",
+                "hidden": true,
+                "properties": [
+                    { "name": "host", "type": "string", "description": "Domain name." },
+                    { "name": "isLocalStorage", "type": "boolean", "description": "True for local storage." },
+                    { "name": "id", "type": "number", "description": "Entry id for further reference." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables storage tracking, storage events will now be delivered to the client."
+            },
+            {
+                "name": "disable",
+                "description": "Disables storage tracking, prevents storage events from being sent to the client."
+            },
+            {
+                "name": "getDOMStorageEntries",
+                "parameters": [
+                    { "name": "storageId", "type": "integer" }
+                ],
+                "returns": [
+                    { "name": "entries", "type": "array", "items": { "$ref": "Entry"} }
+                ]
+            },
+            {
+                "name": "setDOMStorageItem",
+                "parameters": [
+                    { "name": "storageId", "type": "integer" },
+                    { "name": "key", "type": "string" },
+                    { "name": "value", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "success", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "removeDOMStorageItem",
+                "parameters": [
+                    { "name": "storageId", "type": "integer" },
+                    { "name": "key", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "success", "type": "boolean" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "addDOMStorage",
+                "parameters": [
+                    { "name": "storage", "$ref": "Entry" }
+                ]
+            },
+            {
+                "name": "updateDOMStorage",
+                "parameters": [
+                    { "name": "storageId", "type": "integer" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "ApplicationCache",
+        "hidden": true,
+        "types": [
+            {
+                "id": "ApplicationCacheResource",
+                "type": "object",
+                "description": "Detailed application cache resource information.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Resource url." },
+                    { "name": "size", "type": "integer", "description": "Resource size." },
+                    { "name": "type", "type": "string", "description": "Resource type." }
+                ]
+            },
+            {
+                "id": "ApplicationCache",
+                "type": "object",
+                "description": "Detailed application cache information.",
+                "properties": [
+                    { "name": "manifestURL", "type": "string", "description": "Manifest URL." },
+                    { "name": "size", "type": "number", "description": "Application cache size." },
+                    { "name": "creationTime", "type": "number", "description": "Application cache creation time." },
+                    { "name": "updateTime", "type": "number", "description": "Application cache update time." },
+                    { "name": "resources", "type": "array", "items": { "$ref": "ApplicationCacheResource" }, "description": "Application cache resources." }
+                ]
+            },
+            {
+                "id": "FrameWithManifest",
+                "type": "object",
+                "description": "Frame identifier - manifest URL pair.",
+                "properties": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame identifier." },
+                    { "name": "manifestURL", "type": "string", "description": "Manifest URL." },
+                    { "name": "status", "type": "integer", "description": "Application cache status." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "getFramesWithManifests",
+                "returns": [
+                    { "name": "frameIds", "type": "array", "items": { "$ref": "FrameWithManifest" }, "description": "Array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." }
+                ],
+                "description": "Returns array of frame identifiers with manifest urls for each frame containing a document associated with some application cache."
+            },
+            {
+                "name": "enable",
+                "description": "Enables application cache domain notifications."
+            },
+            {
+                "name": "getManifestForFrame",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame containing document whose manifest is retrieved." }
+                ],
+                "returns": [
+                    { "name": "manifestURL", "type": "string", "description": "Manifest URL for document in the given frame." }
+                ],
+                "description": "Returns manifest URL for document in the given frame."
+            },
+            {
+                "name": "getApplicationCacheForFrame",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame containing document whose application cache is retrieved." }
+                ],
+                "returns": [
+                    { "name": "applicationCache", "$ref": "ApplicationCache", "description": "Relevant application cache data for the document in given frame." }
+                ],
+                "description": "Returns relevant application cache data for the document in given frame."
+            }
+        ],
+        "events": [
+            {
+                "name": "applicationCacheStatusUpdated",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame containing document whose application cache updated status." },
+                    { "name": "manifestURL", "type": "string", "description": "Manifest URL." },
+                    { "name": "status", "type": "integer", "description": "Updated application cache status." }
+                ]
+            },
+            {
+                "name": "networkStateUpdated",
+                "parameters": [
+                    { "name": "isNowOnline", "type": "boolean" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "FileSystem",
+        "hidden": true,
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables events from backend."
+            },
+            {
+                "name": "disable",
+                "description": "Disables events from backend.."
+            }
+        ],
+        "events": [
+        ]
+    },
+    {
+        "domain": "DOM",
+        "description": "This domain exposes DOM read/write operations. Each DOM Node is represented with its mirror object that has an <code>id</code>. This <code>id</code> can be used to get additional information on the Node, resolve it into the JavaScript object wrapper, etc. It is important that client receives DOM events only for the nodes that are known to the client. Backend keeps track of the nodes that were sent to the client and never sends the same node twice. It is client's responsibility to collect information about the nodes that were sent to the client.<p>Note that <code>iframe</code> owner elements will return corresponding document elements as their child nodes.</p>",
+        "types": [
+            {
+                "id": "NodeId",
+                "type": "integer",
+                "description": "Unique DOM node identifier."
+            },
+            {
+                "id": "Node",
+                "type": "object",
+                "properties": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Node identifier that is passed into the rest of the DOM messages as the <code>nodeId</code>. Backend will only push node with given <code>id</code> once. It is aware of all requested nodes and will only fire DOM events for nodes known to the client." },
+                    { "name": "nodeType", "type": "integer", "description": "<code>Node</code>'s nodeType." },
+                    { "name": "nodeName", "type": "string", "description": "<code>Node</code>'s nodeName." },
+                    { "name": "localName", "type": "string", "description": "<code>Node</code>'s localName." },
+                    { "name": "nodeValue", "type": "string", "description": "<code>Node</code>'s nodeValue." },
+                    { "name": "childNodeCount", "type": "integer", "optional": true, "description": "Child count for <code>Container</code> nodes." },
+                    { "name": "children", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Child nodes of this node when requested with children." },
+                    { "name": "attributes", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Attributes of the <code>Element</code> node in the form of flat array <code>[name1, value1, name2, value2]</code>." },
+                    { "name": "documentURL", "type": "string", "optional": true, "description": "Document URL that <code>Document</code> or <code>FrameOwner</code> node points to." },
+                    { "name": "publicId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s publicId." },
+                    { "name": "systemId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s systemId." },
+                    { "name": "internalSubset", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s internalSubset." },
+                    { "name": "xmlVersion", "type": "string", "optional": true, "description": "<code>Document</code>'s XML version in case of XML documents." },
+                    { "name": "name", "type": "string", "optional": true, "description": "<code>Attr</code>'s name." },
+                    { "name": "value", "type": "string", "optional": true, "description": "<code>Attr</code>'s value." },
+                    { "name": "contentDocument", "$ref": "Node", "optional": true, "description": "Content document for frame owner elements." }
+                ],
+                "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type."
+            },
+            {
+                "id": "EventListener",
+                "type": "object",
+                "hidden": true,
+                "properties": [
+                    { "name": "type", "type": "string", "description": "<code>EventListener</code>'s type." },
+                    { "name": "useCapture", "type": "boolean", "description": "<code>EventListener</code>'s useCapture." },
+                    { "name": "isAttribute", "type": "boolean", "description": "<code>EventListener</code>'s isAttribute." },
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Target <code>DOMNode</code> id." },
+                    { "name": "handlerBody", "type": "string", "description": "Event handler function body." },
+                    { "name": "location", "$ref": "Debugger.Location", "optional": true, "description": "Handler code location." }
+                ],
+                "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type."
+            },
+            {
+                "id": "RGBA",
+                "type": "object",
+                "properties": [
+                    { "name": "r", "type": "integer", "description": "The red component, in the [0-255] range." },
+                    { "name": "g", "type": "integer", "description": "The green component, in the [0-255] range." },
+                    { "name": "b", "type": "integer", "description": "The blue component, in the [0-255] range." },
+                    { "name": "a", "type": "number", "optional": true, "description": "The alpha component, in the [0-1] range (default: 1)." }
+                ],
+                "description": "A structure holding an RGBA color."
+            },
+            {
+                "id": "HighlightConfig",
+                "type": "object",
+                "properties": [
+                    { "name": "showInfo", "type": "boolean", "optional": true, "description": "Whether the node info tooltip should be shown (default: false)." },
+                    { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." },
+                    { "name": "paddingColor", "$ref": "RGBA", "optional": true, "description": "The padding highlight fill color (default: transparent)." },
+                    { "name": "borderColor", "$ref": "RGBA", "optional": true, "description": "The border highlight fill color (default: transparent)." },
+                    { "name": "marginColor", "$ref": "RGBA", "optional": true, "description": "The margin highlight fill color (default: transparent)." }
+                ],
+                "description": "Configuration data for the highlighting of page elements."
+            }
+        ],
+        "commands": [
+            {
+                "name": "getDocument",
+                "returns": [
+                    { "name": "root", "$ref": "Node", "description": "Resulting node." }
+                ],
+                "description": "Returns the root DOM node to the caller."
+            },
+            {
+                "name": "requestChildNodes",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get children for." }
+                ],
+                "description": "Requests that children of the node with given id are returned to the caller in form of <code>setChildNodes</code> events."
+            },
+            {
+                "name": "querySelector",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." },
+                    { "name": "selector", "type": "string", "description": "Selector string." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Query selector result." }
+                ],
+                "description": "Executes <code>querySelector</code> on a given node."
+            },
+            {
+                "name": "querySelectorAll",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." },
+                    { "name": "selector", "type": "string", "description": "Selector string." }
+                ],
+                "returns": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Query selector result." }
+                ],
+                "description": "Executes <code>querySelectorAll</code> on a given node."
+            },
+            {
+                "name": "setNodeName",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set name for." },
+                    { "name": "name", "type": "string", "description": "New node's name." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "New node's id." }
+                ],
+                "description": "Sets node name for a node with given id."
+            },
+            {
+                "name": "setNodeValue",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set value for." },
+                    { "name": "value", "type": "string", "description": "New node's value." }
+                ],
+                "description": "Sets node value for a node with given id."
+            },
+            {
+                "name": "removeNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to remove." }
+                ],
+                "description": "Removes node with given id."
+            },
+            {
+                "name": "setAttributeValue",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attribute for." },
+                    { "name": "name", "type": "string", "description": "Attribute name." },
+                    { "name": "value", "type": "string", "description": "Attribute value." }
+                ],
+                "description": "Sets attribute for an element with given id."
+            },
+            {
+                "name": "setAttributesAsText",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attributes for." },
+                    { "name": "text", "type": "string", "description": "Text with a number of attributes. Will parse this text using HTML parser." },
+                    { "name": "name", "type": "string", "optional": true, "description": "Attribute name to replace with new attributes derived from text in case text parsed successfully." }
+                ],
+                "description": "Sets attributes on element with given id. This method is useful when user edits some existing attribute value and types in several attribute name/value pairs."
+            },
+            {
+                "name": "removeAttribute",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to remove attribute from." },
+                    { "name": "name", "type": "string", "description": "Name of the attribute to remove." }
+                ],
+                "description": "Removes attribute with given name from an element with given id."
+            },
+            {
+                "name": "getEventListenersForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get listeners for." }
+                ],
+                "returns": [
+                    { "name": "listeners", "type": "array", "items": { "$ref": "EventListener"}, "description": "Array of relevant listeners." }
+                ],
+                "description": "Returns event listeners relevant to the node.",
+                "hidden": true
+            },
+            {
+                "name": "getOuterHTML",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get markup for." }
+                ],
+                "returns": [
+                    { "name": "outerHTML", "type": "string", "description": "Outer HTML markup." }
+                ],
+                "description": "Returns node's HTML markup."
+            },
+            {
+                "name": "setOuterHTML",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set markup for." },
+                    { "name": "outerHTML", "type": "string", "description": "Outer HTML markup to set." }
+                ],
+                "description": "Sets node HTML markup, returns new node id."
+            },
+            {
+                "name": "performSearch",
+                "parameters": [
+                    { "name": "query", "type": "string", "description": "Plain text or query selector or XPath search query." }
+                ],
+                "returns": [
+                    { "name": "searchId", "type": "string", "description": "Unique search session identifier." },
+                    { "name": "resultCount", "type": "integer", "description": "Number of search results." }
+                ],
+                "description": "Searches for a given string in the DOM tree. Use <code>getSearchResults</code> to access search results or <code>cancelSearch</code> to end this search session.",
+                "hidden": true
+            },
+            {
+                "name": "getSearchResults",
+                "parameters": [
+                    { "name": "searchId", "type": "string", "description": "Unique search session identifier." },
+                    { "name": "fromIndex", "type": "integer", "description": "Start index of the search result to be returned." },
+                    { "name": "toIndex", "type": "integer", "description": "End index of the search result to be returned." }
+                ],
+                "returns": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the search result nodes." }
+                ],
+                "description": "Returns search results from given <code>fromIndex</code> to given <code>toIndex</code> from the sarch with the given identifier.",
+                "hidden": true
+            },
+            {
+                "name": "discardSearchResults",
+                "parameters": [
+                    { "name": "searchId", "type": "string", "description": "Unique search session identifier." }
+                ],
+                "description": "Discards search results from the session with the given id. <code>getSearchResults</code> should no longer be called for that search.",
+                "hidden": true
+            },
+            {
+                "name": "requestNode",
+                "parameters": [
+                    { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "JavaScript object id to convert into node." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Node id for given object." }
+                ],
+                "description": "Requests that the node is sent to the caller given the JavaScript node object reference. All nodes that form the path from the node to the root are also sent to the client as a series of <code>setChildNodes</code> notifications."
+            },
+            {
+                "name": "setInspectModeEnabled",
+                "hidden": true,
+                "parameters": [
+                    { "name": "enabled", "type": "boolean", "description": "True to enable inspection mode, false to disable it." },
+                    { "name": "highlightConfig", "$ref": "HighlightConfig", "optional": true, "description": "A descriptor for the highlight appearance of hovered-over nodes. May be omitted if <code>enabled == false</code>." }
+                ],
+                "description": "Enters the 'inspect' mode. In this mode, elements that user is hovering over are highlighted. Backend then generates 'inspect' command upon element selection."
+            },
+            {
+                "name": "highlightRect",
+                "parameters": [
+                    { "name": "x", "type": "integer", "description": "X coordinate" },
+                    { "name": "y", "type": "integer", "description": "Y coordinate" },
+                    { "name": "width", "type": "integer", "description": "Rectangle width" },
+                    { "name": "height", "type": "integer", "description": "Rectangle height" },
+                    { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." },
+                    { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." }
+                ],
+                "description": "Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport."
+            },
+            {
+                "name": "highlightNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Identifier of the node to highlight." },
+                    { "name": "highlightConfig", "$ref": "HighlightConfig", "description": "A descriptor for the highlight appearance." }
+                ],
+                "description": "Highlights DOM node with given id."
+            },
+            {
+                "name": "hideHighlight",
+                "description": "Hides DOM node highlight."
+            },
+            {
+                "name": "highlightFrame",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame to highlight." },
+                    { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." },
+                    { "name": "contentOutlineColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight outline color (default: transparent)." }
+                ],
+                "description": "Highlights owner element of the frame with given id.",
+                "hidden": true
+            },
+            {
+                "name": "pushNodeByPathToFrontend",
+                "parameters": [
+                    { "name": "path", "type": "string", "description": "Path to node in the proprietary format." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node for given path." }
+                ],
+                "description": "Requests that the node is sent to the caller given its path. // FIXME, use XPath",
+                "hidden": true
+            },
+            {
+                "name": "resolveNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to resolve." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }
+                ],
+                "returns": [
+                    { "name": "object", "$ref": "Runtime.RemoteObject", "description": "JavaScript object wrapper for given node." }
+                ],
+                "description": "Resolves JavaScript node object for given node id."
+            },
+            {
+                "name": "getAttributes",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to retrieve attibutes for." }
+                ],
+                "returns": [
+                    { "name": "attributes", "type": "array", "items": { "type": "string" }, "description": "An interleaved array of node attribute names and values." }
+                ],
+                "description": "Returns attributes for the specified node."
+            },
+            {
+                "name": "moveTo",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to drop." },
+                    { "name": "targetNodeId", "$ref": "NodeId", "description": "Id of the element to drop into." },
+                    { "name": "insertBeforeNodeId", "$ref": "NodeId", "optional": true, "description": "Drop node before given one." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "New id of the moved node." }
+                ],
+                "description": "Moves node into the new container, places it before the given anchor."
+            }
+        ],
+        "events": [
+            {
+                "name": "documentUpdated",
+                "description": "Fired when <code>Document</code> has been totally updated. Node ids are no longer valid."
+            },
+            {
+                "name": "setChildNodes",
+                "parameters": [
+                    { "name": "parentId", "$ref": "NodeId", "description": "Parent node id to populate with children." },
+                    { "name": "nodes", "type": "array", "items": { "$ref": "Node"}, "description": "Child nodes array." }
+                ],
+                "description": "Fired when backend wants to provide client with the missing DOM structure. This happens upon most of the calls requesting node ids."
+            },
+            {
+                "name": "attributeModified",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "name", "type": "string", "description": "Attribute name." },
+                    { "name": "value", "type": "string", "description": "Attribute value." }
+                ],
+                "description": "Fired when <code>Element</code>'s attribute is modified."
+            },
+            {
+                "name": "attributeRemoved",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "name", "type": "string", "description": "A ttribute name." }
+                ],
+                "description": "Fired when <code>Element</code>'s attribute is removed."
+            },
+            {
+                "name": "inlineStyleInvalidated",
+                "parameters": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the nodes for which the inline styles have been invalidated." }
+                ],
+                "description": "Fired when <code>Element</code>'s inline style is modified via a CSS property modification.",
+                "hidden": true
+            },
+            {
+                "name": "characterDataModified",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "characterData", "type": "string", "description": "New text value." }
+                ],
+                "description": "Mirrors <code>DOMCharacterDataModified</code> event."
+            },
+            {
+                "name": "childNodeCountUpdated",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "childNodeCount", "type": "integer", "description": "New node count." }
+                ],
+                "description": "Fired when <code>Container</code>'s child node count has changed."
+            },
+            {
+                "name": "childNodeInserted",
+                "parameters": [
+                    { "name": "parentNodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "previousNodeId", "$ref": "NodeId", "description": "If of the previous siblint." },
+                    { "name": "node", "$ref": "Node", "description": "Inserted node data." }
+                ],
+                "description": "Mirrors <code>DOMNodeInserted</code> event."
+            },
+            {
+                "name": "childNodeRemoved",
+                "parameters": [
+                    { "name": "parentNodeId", "$ref": "NodeId", "description": "Parent id." },
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has been removed." }
+                ],
+                "description": "Mirrors <code>DOMNodeRemoved</code> event."
+            }
+        ]
+    },
+    {
+        "domain": "CSS",
+        "hidden": true,
+        "description": "This domain exposes CSS read/write operations. All CSS objects, like stylesheets, rules, and styles, have an associated <code>id</code> used in subsequent operations on the related object. Each object type has a specific <code>id</code> structure, and those are not interchangeable between objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls (which accept a DOM node id). Alternatively, a client can discover all the existing stylesheets with the <code>getAllStyleSheets()</code> method and subsequently load the required stylesheet contents using the <code>getStyleSheet[Text]()</code> methods.",
+        "types": [
+            {
+                "id": "CSSStyleId",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "type": "string", "description": "Enclosing stylesheet identifier." },
+                    { "name": "ordinal", "type": "integer", "description": "The style ordinal within the stylesheet." }
+                ],
+                "description": "This object identifies a CSS style in a unique way."
+            },
+            {
+                "id": "CSSRuleId",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "type": "string", "description": "Enclosing stylesheet identifier." },
+                    { "name": "ordinal", "type": "integer", "description": "The rule ordinal within the stylesheet." }
+                ],
+                "description": "This object identifies a CSS rule in a unique way."
+            },
+            {
+                "id": "PseudoIdRules",
+                "type": "object",
+                "properties": [
+                    { "name": "pseudoId", "type": "integer", "description": "Pseudo style identifier (see <code>enum PseudoId</code> in <code>RenderStyleConstants.h</code>)."},
+                    { "name": "rules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "CSS rules applicable to the pseudo style."}
+                ],
+                "description": "CSS rule collection for a single pseudo style."
+            },
+            {
+                "id": "InheritedStyleEntry",
+                "type": "object",
+                "properties": [
+                    { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "The ancestor node's inline style, if any, in the style inheritance chain." },
+                    { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "CSS rules matching the ancestor node in the style inheritance chain." }
+                ],
+                "description": "CSS rule collection for a single pseudo style."
+            },
+            {
+                "id": "CSSStyleAttribute",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "DOM attribute name (e.g. \"width\")."},
+                    { "name": "style", "$ref": "CSSStyle", "description": "CSS style generated by the respective DOM attribute."}
+                ],
+                "description": "CSS style information for a DOM style attribute."
+            },
+            {
+                "id": "CSSStyleSheetHeader",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "type": "string", "description": "The stylesheet identifier."},
+                    { "name": "sourceURL", "type": "string", "description": "Stylesheet resource URL."},
+                    { "name": "title", "type": "string", "description": "Stylesheet title."},
+                    { "name": "disabled", "type": "boolean", "description": "Denotes whether the stylesheet is disabled."}
+                ],
+                "description": "CSS stylesheet metainformation."
+            },
+            {
+                "id": "CSSStyleSheetBody",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "type": "string", "description": "The stylesheet identifier."},
+                    { "name": "rules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "Stylesheet resource URL."},
+                    { "name": "text", "type": "string", "optional": true, "description": "Stylesheet resource contents (if available)."}
+                ],
+                "description": "CSS stylesheet contents."
+            },
+            {
+                "id": "CSSRule",
+                "type": "object",
+                "properties": [
+                    { "name": "ruleId", "$ref": "CSSRuleId", "optional": true, "description": "The CSS rule identifier (absent for user agent stylesheet and user-specified stylesheet rules)."},
+                    { "name": "selectorText", "type": "string", "description": "Rule selector."},
+                    { "name": "sourceURL", "type": "string", "optional": true, "description": "Parent stylesheet resource URL (for regular rules)."},
+                    { "name": "sourceLine", "type": "integer", "description": "Line ordinal of the rule selector start character in the resource."},
+                    { "name": "origin", "type": "string", "enum": ["user", "user-agent", "inspector", "regular"], "description": "The parent stylesheet type: \"user\" for user stylesheets, \"user-agent\" for user-agent stylesheets, \"inspector\" for stylesheets created by the inspector (i.e. those holding new rules created with <code>addRule()</code>), \"regular\" for regular stylesheets."},
+                    { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." },
+                    { "name": "selectorRange", "$ref": "SourceRange", "optional": true, "description": "The rule selector range in the underlying resource (if available)." },
+                    { "name": "media", "type": "array", "items": { "$ref": "CSSMedia" }, "optional": true, "description": "Media list array (for rules involving media queries). The array enumerates media queries starting with the innermost one, going outwards." }
+                ],
+                "description": "CSS rule representation."
+            },
+            {
+                "id": "SourceRange",
+                "type": "object",
+                "properties": [
+                    { "name": "start", "type": "integer", "description": "Start of range (inclusive)."},
+                    { "name": "end", "type": "integer", "description": "End of range (exclusive)."}
+                ],
+                "description": "Text range within a resource."
+            },
+            {
+                "id": "ShorthandEntry",
+                "type": "object"
+            },
+            {
+                "id": "CSSComputedStyleProperty",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Computed style property name." },
+                    { "name": "value", "type": "string", "description": "Computed style property value." }
+                ]
+            },            
+            {
+                "id": "CSSStyle",
+                "type": "object",
+                "properties": [
+                    { "name": "styleId", "$ref": "CSSStyleId", "optional": true, "description": "The CSS style identifier (absent for attribute styles)." },
+                    { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSProperty" }, "description": "CSS properties in the style." },
+                    { "name": "shorthandEntries", "type": "array", "items": { "$ref": "ShorthandEntry" }, "description": "Computed values for all shorthands found in the style." },
+                    { "name": "cssText", "type": "string", "optional": true, "description": "Style declaration text (if available)." },
+                    { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Style declaration range in the enclosing stylesheet (if available)." },
+                    { "name": "width", "type": "string", "optional": true, "description": "The effective \"width\" property value from this style." },
+                    { "name": "height", "type": "string", "optional": true, "description": "The effective \"height\" property value from this style." }
+                ],
+                "description": "CSS style representation."
+            },
+            {
+                "id": "CSSProperty",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "The property name." },
+                    { "name": "value", "type": "string", "description": "The property value." },
+                    { "name": "priority", "type": "string", "optional": true, "description": "The property priority (implies \"\" if absent)." },
+                    { "name": "implicit", "type": "boolean", "optional": true, "description": "Whether the property is implicit (implies <code>false</code> if absent)." },
+                    { "name": "text", "type": "string", "optional": true, "description": "The full property text as specified in the style." },
+                    { "name": "parsedOk", "type": "boolean", "optional": true, "description": "Whether the property is understood by the browser (implies <code>true</code> if absent)." },
+                    { "name": "status", "type": "string", "enum": ["active", "inactive", "disabled", "style"], "optional": true, "description": "The property status: \"active\" (implied if absent) if the property is effective in the style, \"inactive\" if the property is overridden by a same-named property in this style later on, \"disabled\" if the property is disabled by the user, \"style\" if the property is reported by the browser rather than by the CSS source parser." },
+                    { "name": "shorthandName", "type": "string", "optional": true, "description": "The related shorthand property name (absent if this property is not a longhand)." },
+                    { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The entire property range in the enclosing style declaration (if available)." }
+                ],
+                "description": "CSS style effective visual dimensions and source offsets."
+            },
+            {
+                "id": "CSSMedia",
+                "type": "object",
+                "properties": [
+                    { "name": "text", "type": "string", "description": "Media query text." },
+                    { "name": "source", "type": "string", "enum": ["mediaRule", "importRule", "linkedSheet", "inlineSheet"], "description": "Source of the media query: \"mediaRule\" if specified by a @media rule, \"importRule\" if specified by an @import rule, \"linkedSheet\" if specified by a \"media\" attribute in a linked stylesheet's LINK tag, \"inlineSheet\" if specified by a \"media\" attribute in an inline stylesheet's STYLE tag." },
+                    { "name": "sourceURL", "type": "string", "optional": true, "description": "URL of the document containing the media query description." },
+                    { "name": "sourceLine", "type": "integer", "optional": true, "description": "Line in the document containing the media query (not defined for the \"stylesheet\" source)." }
+                ],
+                "description": "CSS media query descriptor."
+            },
+            {
+                "id": "SelectorProfileEntry",
+                "type": "object",
+                "properties": [
+                    { "name": "selector", "type": "string", "description": "CSS selector of the corresponding rule." },
+                    { "name": "url", "type": "string", "description": "URL of the resource containing the corresponding rule." },
+                    { "name": "lineNumber", "type": "integer", "description": "Selector line number in the resource for the corresponding rule." },
+                    { "name": "time", "type": "number", "description": "Total time this rule handling contributed to the browser running time during profiling (in milliseconds.)" },
+                    { "name": "hitCount", "type": "integer", "description": "Number of times this rule was considered a candidate for matching against DOM elements." },
+                    { "name": "matchCount", "type": "integer", "description": "Number of times this rule actually matched a DOM element." }
+                ],
+                "description": "CSS selector profile entry."
+            },
+            {
+                "id": "SelectorProfile",
+                "type": "object",
+                "properties": [
+                    { "name": "totalTime", "type": "number", "description": "Total processing time for all selectors in the profile (in milliseconds.)" },
+                    { "name": "data", "type": "array", "items": { "$ref": "SelectorProfileEntry" }, "description": "CSS selector profile entries." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables the CSS agent for the given page. Clients should not assume that the CSS agent has been enabled until the result of this command is received."
+            },
+            {
+                "name": "disable",
+                "description": "Disables the CSS agent for the given page."
+            },
+            {
+                "name": "getMatchedStylesForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId" },
+                    { "name": "forcedPseudoClasses", "type": "array", "items": { "type": "string", "enum": ["active", "focus", "hover", "visited"] }, "optional": true, "description": "Element pseudo classes to force when computing applicable style rules." },
+                    { "name": "includePseudo", "type": "boolean", "optional": true, "description": "Whether to include pseudo styles (default: true)." },
+                    { "name": "includeInherited", "type": "boolean", "optional": true, "description": "Whether to include inherited styles (default: true)." }
+                ],
+                "returns": [
+                    { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "CSSRule" }, "optional": true, "description": "CSS rules matching this node, from all applicable stylesheets." },
+                    { "name": "pseudoElements", "type": "array", "items": { "$ref": "PseudoIdRules" }, "optional": true, "description": "Pseudo style rules for this node." },
+                    { "name": "inherited", "type": "array", "items": { "$ref": "InheritedStyleEntry" }, "optional": true, "description": "A chain of inherited styles (from the immediate node parent up to the DOM tree root)." }
+                ],
+                "description": "Returns requested styles for a DOM node identified by <code>nodeId</code>."
+            },
+            {
+                "name": "getInlineStylesForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId" }
+                ],
+                "returns": [
+                    { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "Inline style for the specified DOM node." },
+                    { "name": "styleAttributes", "type": "array", "items": { "$ref": "CSSStyleAttribute" }, "optional": true, "description": "Entries for style-related element attributes (e.g. width=20)."}
+                ],
+                "description": "Returns the styles defined inline (explicitly in the \"style\" attribute and implicitly, using DOM attributes) for a DOM node identified by <code>nodeId</code>."
+            },
+            {
+                "name": "getComputedStyleForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId" },
+                    { "name": "forcedPseudoClasses", "type": "array", "items": { "type": "string", "enum": ["active", "focus", "hover", "visited"] }, "optional": true, "description": "Element pseudo classes to force when computing applicable style rules." }
+                ],
+                "returns": [
+                    { "name": "computedStyle", "type": "array", "items": { "$ref": "CSSComputedStyleProperty" }, "description": "Computed style for the specified DOM node." }
+                ],
+                "description": "Returns the computed style for a DOM node identified by <code>nodeId</code>."
+            },
+            {
+                "name": "getAllStyleSheets",
+                "returns": [
+                    { "name": "headers", "type": "array", "items": { "$ref": "CSSStyleSheetHeader" }, "description": "Descriptor entries for all available stylesheets." }
+                ],
+                "description": "Returns metainfo entries for all known stylesheets."
+            },
+            {
+                "name": "getStyleSheet",
+                "parameters": [
+                    { "name": "styleSheetId", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "styleSheet", "$ref": "CSSStyleSheetBody", "description": "Stylesheet contents for the specified <code>styleSheetId</code>." }
+                ],
+                "description": "Returns stylesheet data for the specified <code>styleSheetId</code>."
+            },
+            {
+                "name": "getStyleSheetText",
+                "parameters": [
+                    { "name": "styleSheetId", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "text", "type": "string", "description": "The stylesheet text." }
+                ],
+                "description": "Returns the current textual content and the URL for a stylesheet."
+            },
+            {
+                "name": "setStyleSheetText",
+                "parameters": [
+                    { "name": "styleSheetId", "type": "string" },
+                    { "name": "text", "type": "string" }
+                ],
+                "description": "Sets the new stylesheet text, thereby invalidating all existing <code>CSSStyleId</code>'s and <code>CSSRuleId</code>'s contained by this stylesheet."
+            },
+            {
+                "name": "setPropertyText",
+                "parameters": [
+                    { "name": "styleId", "$ref": "CSSStyleId" },
+                    { "name": "propertyIndex", "type": "integer" },
+                    { "name": "text", "type": "string" },
+                    { "name": "overwrite", "type": "boolean" }
+                ],
+                "returns": [
+                    { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property text modification." }
+                ],
+                "description": "Sets the new <code>text</code> for a property in the respective style, at offset <code>propertyIndex</code>. If <code>overwrite</code> is <code>true</code>, a property at the given offset is overwritten, otherwise inserted. <code>text</code> entirely replaces the property <code>name: value</code>."
+            },
+            {
+                "name": "toggleProperty",
+                "parameters": [
+                    { "name": "styleId", "$ref": "CSSStyleId" },
+                    { "name": "propertyIndex", "type": "integer" },
+                    { "name": "disable", "type": "boolean" }
+                ],
+                "returns": [
+                    { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property toggling." }
+                ],
+                "description": "Toggles the property in the respective style, at offset <code>propertyIndex</code>. The <code>disable</code> parameter denotes whether the property should be disabled (i.e. removed from the style declaration). If <code>disable == false</code>, the property gets put back into its original place in the style declaration."
+            },
+            {
+                "name": "setRuleSelector",
+                "parameters": [
+                    { "name": "ruleId", "$ref": "CSSRuleId" },
+                    { "name": "selector", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "rule", "$ref": "CSSRule", "description": "The resulting rule after the selector modification." }
+                ],
+                "description": "Modifies the rule selector."
+            },
+            {
+                "name": "addRule",
+                "parameters": [
+                    { "name": "contextNodeId", "$ref": "DOM.NodeId" },
+                    { "name": "selector", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "rule", "$ref": "CSSRule", "description": "The newly created rule." }
+                ],
+                "description": "Creates a new empty rule with the given <code>selector</code> in a special \"inspector\" stylesheet in the owner document of the context node."
+            },
+            {
+                "name": "getSupportedCSSProperties",
+                "returns": [
+                    { "name": "cssProperties", "type": "array", "items": { "type": "string" }, "description": "Supported property names." }
+                ],
+                "description": "Returns all supported CSS property names."
+            },
+            {
+                "name": "startSelectorProfiler"
+            },
+            {
+                "name": "stopSelectorProfiler",
+                "returns": [
+                    { "name": "profile", "$ref": "SelectorProfile" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "mediaQueryResultChanged",
+                "description": "Fires whenever a MediaQuery result changes (for example, after a browser window has been resized.) The current implementation considers only viewport-dependent media features."
+            }
+        ]
+    },
+    {
+        "domain": "Timeline",
+        "description": "Timeline provides its clients with instrumentation records that are generated during the page runtime. Timeline instrumentation can be started and stopped using corresponding commands. While timeline is started, it is generating timeline event records.",
+        "types": [
+            {
+                "id": "TimelineEvent",
+                "type": "object",
+                "properties": [
+                    { "name": "type", "type": "string", "description": "Event type." },
+                    { "name": "data", "type": "object", "description": "Event data." },
+                    { "name": "children", "type": "array", "optional": true, "items": { "$ref": "TimelineEvent" }, "description": "Nested records." }
+                ],
+                "description": "Timeline record contains information about the recorded activity."
+            }
+        ],
+        "commands": [
+            {
+                "name": "start",
+                "parameters": [
+                    { "name": "maxCallStackDepth", "optional": true, "type": "integer", "description": "Samples JavaScript stack traces up to <code>maxCallStackDepth</code>, defaults to 5." }
+                ],
+                "description": "Starts capturing instrumentation events."
+            },
+            {
+                "name": "stop",
+                "description": "Stops capturing instrumentation events."
+            },
+            {
+                "name": "setIncludeMemoryDetails",
+                "parameters": [
+                    { "name": "enabled", "type": "boolean", "description": "True to start collecting DOM counters." }
+                ],
+                "hidden": true,
+                "description": "Starts calculating various DOM statistics and sending them as part of timeline events."
+            }
+        ],
+        "events": [
+            {
+                "name": "eventRecorded",
+                "parameters": [
+                    { "name": "record", "$ref": "TimelineEvent", "description": "Timeline event record data." }
+                ],
+                "description": "Fired for every instrumentation event while timeline is started."
+            }
+        ]
+    },
+    {
+        "domain": "Debugger",
+        "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.",
+        "types": [
+            {
+                "id": "BreakpointId",
+                "type": "string",
+                "description": "Breakpoint identifier."
+            },
+            {
+                "id": "ScriptId",
+                "type": "string",
+                "description": "Unique script identifier."
+            },
+            {
+                "id": "CallFrameId",
+                "type": "string",
+                "description": "Call frame identifier."
+            },
+            {
+                "id": "Location",
+                "type": "object",
+                "properties": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." },
+                    { "name": "lineNumber", "type": "integer", "description": "Line number in the script." },
+                    { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script." }
+                ],
+                "description": "Location in the source code."
+            },
+            {
+                "id": "FunctionDetails",
+                "hidden": true,
+                "type": "object",
+                "properties": [
+                    { "name": "location", "$ref": "Location", "description": "Location of the function." },
+                    { "name": "name", "type": "string", "optional": true, "description": "Name of the function. Not present for anonymous functions." },
+                    { "name": "displayName", "type": "string", "optional": true, "description": "Display name of the function(specified in 'displayName' property on the function object)." },
+                    { "name": "inferredName", "type": "string", "optional": true, "description": "Name of the function inferred from its initial assignment." }
+                ],
+                "description": "Information about the function."
+            },
+            {
+                "id": "CallFrame",
+                "type": "object",
+                "properties": [
+                    { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." },
+                    { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." },
+                    { "name": "location", "$ref": "Location", "description": "Location in the source code." },
+                    { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." },
+                    { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." }
+                ],
+                "description": "JavaScript call frame. Array of call frames form the call stack."
+            },
+            {
+                "id": "Scope",
+                "type": "object",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch"], "description": "Scope type." },
+                    { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }
+                ],
+                "description": "Scope description."
+            }
+        ],
+        "commands": [
+            {
+                "name": "causesRecompilation",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "True if enabling debugger causes scripts recompilation." }
+                ],
+                "hidden": true,
+                "description": "Tells whether enabling debugger causes scripts recompilation."
+            },
+            {
+                "name": "supportsNativeBreakpoints",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "True if debugger supports native breakpoints." }
+                ],
+                "hidden": true,
+                "description": "Tells whether debugger supports native breakpoints."
+            },
+            {
+                "name": "enable",
+                "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received."
+            },
+            {
+                "name": "disable",
+                "description": "Disables debugger for given page."
+            },
+            {
+                "name": "setBreakpointsActive",
+                "parameters": [
+                    { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." }
+                ],
+                "description": "Activates / deactivates all breakpoints on the page."
+            },
+            {
+                "name": "setBreakpointByUrl",
+                "parameters": [
+                    { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." },
+                    { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." },
+                    { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." },
+                    { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." },
+                    { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
+                ],
+                "returns": [
+                    { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
+                    { "name": "locations", "optional": true, "type": "array", "items": { "$ref": "Location"}, "description": "List of the locations this breakpoint resolved into upon addition." }
+                ],
+                "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads."
+            },
+            {
+                "name": "setBreakpoint",
+                "parameters": [
+                    { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." },
+                    { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
+                ],
+                "returns": [
+                    { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
+                    { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." }
+                ],
+                "description": "Sets JavaScript breakpoint at a given location."
+            },
+            {
+                "name": "removeBreakpoint",
+                "parameters": [
+                    { "name": "breakpointId", "$ref": "BreakpointId" }
+                ],
+                "description": "Removes JavaScript breakpoint."
+            },
+            {
+                "name": "continueToLocation",
+                "parameters": [
+                    { "name": "location", "$ref": "Location", "description": "Location to continue to." }
+                ],
+                "description": "Continues execution until specific location is reached."
+            },
+            {
+                "name": "stepOver",
+                "description": "Steps over the statement."
+            },
+            {
+                "name": "stepInto",
+                "description": "Steps into the function call."
+            },
+            {
+                "name": "stepOut",
+                "description": "Steps out of the function call."
+            },
+            {
+                "name": "pause",
+                "description": "Stops on the next JavaScript statement."
+            },
+            {
+                "name": "resume",
+                "description": "Resumes JavaScript execution."
+            },
+            {
+                "name": "searchInContent",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to search in." },
+                    { "name": "query", "type": "string", "description": "String to search for."  },
+                    { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." },
+                    { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "Page.SearchMatch" }, "description": "List of search matches." }
+                ],
+                "description": "Searches for given string in script content."
+            },
+            {
+                "name": "canSetScriptSource",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "True if <code>setScriptSource</code> is supported." }
+                ],
+                "description": "Tells whether <code>setScriptSource</code> is supported."
+            },
+            {
+                "name": "setScriptSource",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to edit." },
+                    { "name": "scriptSource", "type": "string", "description": "New content of the script." },
+                    { "name": "preview", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", "hidden": true }
+                ],
+                "returns": [
+                    { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame"}, "description": "New stack trace in case editing has happened while VM was stopped." },
+                    { "name": "result", "type": "object", "optional": true, "description": "VM-specific description of the changes applied.", "hidden": true }
+                ],
+                "description": "Edits JavaScript source live."
+            },
+            {
+                "name": "getScriptSource",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to get source for." }
+                ],
+                "returns": [
+                    { "name": "scriptSource", "type": "string", "description": "Script source." }
+                ],
+                "description": "Returns source for the script with given id."
+            },
+            {
+                "name": "getFunctionDetails",
+                "hidden": true,
+                "parameters": [
+                    { "name": "functionId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the function to get location for." }
+                ],
+                "returns": [
+                    { "name": "details", "$ref": "FunctionDetails", "description": "Information about the function." }
+                ],
+                "description": "Returns detailed informtation on given function."
+            },
+            {
+                "name": "setPauseOnExceptions",
+                "parameters": [
+                    { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." }
+                ],
+                "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>."
+            },
+            {
+                "name": "evaluateOnCallFrame",
+                "parameters": [
+                    { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." },
+                    { "name": "expression", "type": "string", "description": "Expression to evaluate." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." },
+                    { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true },
+                    { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ],
+                "description": "Evaluates expression on a given call frame."
+            }
+        ],
+        "events": [
+            {
+                "name": "globalObjectCleared",
+                "description": "Called when global has been cleared and debugger client should reset its state. Happens upon navigation or reload."
+            },
+            {
+                "name": "scriptParsed",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Identifier of the script parsed." },
+                    { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." },
+                    { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." },
+                    { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." },
+                    { "name": "endLine", "type": "integer", "description": "Last line of the script." },
+                    { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." },
+                    { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." },
+                    { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }
+                ],
+                "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger."
+            },
+            {
+                "name": "scriptFailedToParse",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL of the script that failed to parse." },
+                    { "name": "scriptSource", "type": "string", "description": "Source text of the script that failed to parse." },
+                    { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource." },
+                    { "name": "errorLine", "type": "integer", "description": "Line with error." },
+                    { "name": "errorMessage", "type": "string", "description": "Parse error message." }
+                ],
+                "description": "Fired when virtual machine fails to parse the script."
+            },
+            {
+                "name": "breakpointResolved",
+                "parameters": [
+                    { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." },
+                    { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." }
+                ],
+                "description": "Fired when breakpoint is resolved to an actual script and location."
+            },
+            {
+                "name": "paused",
+                "parameters": [
+                    { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." },
+                    { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "other" ], "description": "Pause reason." },
+                    { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }
+                ],
+                "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria."
+            },
+            {
+                "name": "resumed",
+                "description": "Fired when the virtual machine resumed execution."
+            }
+        ]
+    },
+    {
+        "domain": "DOMDebugger",
+        "description": "DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript execution will stop on these operations as if there was a regular breakpoint set.",
+        "types": [
+            {
+                "id": "DOMBreakpointType",
+                "type": "string",
+                "enum": ["subtree-modified", "attribute-modified", "node-removed"],
+                "description": "DOM breakpoint type."
+            }
+        ],
+        "commands": [
+            {
+                "name": "setDOMBreakpoint",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to set breakpoint on." },
+                    { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the operation to stop upon." }
+                ],
+                "description": "Sets breakpoint on particular operation with DOM."
+            },
+            {
+                "name": "removeDOMBreakpoint",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to remove breakpoint from." },
+                    { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the breakpoint to remove." }
+                ],
+                "description": "Removes DOM breakpoint that was set using <code>setDOMBreakpoint</code>."
+            },
+            {
+                "name": "setEventListenerBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "DOM Event name to stop on (any DOM event will do)." }
+                ],
+                "description": "Sets breakpoint on particular DOM event."
+            },
+            {
+                "name": "removeEventListenerBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "Event name." }
+                ],
+                "description": "Removes breakpoint on particular DOM event."
+            },
+            {
+                "name": "setInstrumentationBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." }
+                ],
+                "description": "Sets breakpoint on particular native event.",
+                "hidden": true
+            },
+            {
+                "name": "removeInstrumentationBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." }
+                ],
+                "description": "Sets breakpoint on particular native event.",
+                "hidden": true
+            },
+            {
+                "name": "setXHRBreakpoint",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "Resource URL substring. All XHRs having this substring in the URL will get stopped upon." }
+                ],
+                "description": "Sets breakpoint on XMLHttpRequest."
+            },
+            {
+                "name": "removeXHRBreakpoint",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "Resource URL substring." }
+                ],
+                "description": "Removes breakpoint from XMLHttpRequest."
+            }
+        ]
+    },
+    {
+        "domain": "Profiler",
+        "hidden": true,
+        "types": [
+            {
+                "id": "Profile",
+                "type": "object",
+                "description": "Profile."
+            },
+            {
+                "id": "ProfileHeader",
+                "type": "object",
+                "description": "Profile header."
+            }
+        ],
+        "commands": [
+            {
+                "name": "causesRecompilation",
+                "returns": [
+                    { "name": "result", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "isSampling",
+                "returns": [
+                    { "name": "result", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "hasHeapProfiler",
+                "returns": [
+                    { "name": "result", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "enable"
+            },
+            {
+                "name": "disable"
+            },
+            {
+                "name": "start"
+            },
+            {
+                "name": "stop"
+            },
+            {
+                "name": "getProfileHeaders",
+                "returns": [
+                    { "name": "headers", "type": "array", "items": { "$ref": "ProfileHeader"} }
+                ]
+            },
+            {
+                "name": "getProfile",
+                "parameters": [
+                    { "name": "type", "type": "string" },
+                    { "name": "uid", "type": "integer" }
+                ],
+                "returns": [
+                    { "name": "profile", "$ref": "Profile" }
+                ]
+            },
+            {
+                "name": "removeProfile",
+                "parameters": [
+                    { "name": "type", "type": "string" },
+                    { "name": "uid", "type": "integer" }
+                ]
+            },
+            {
+                "name": "clearProfiles"
+            },
+            {
+                "name": "takeHeapSnapshot"
+            },
+            {
+                "name": "collectGarbage"
+            },
+            {
+                "name": "getObjectByHeapObjectId",
+                "parameters": [
+                    { "name": "objectId", "type": "integer" }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "addProfileHeader",
+                "parameters": [
+                    { "name": "header", "$ref": "ProfileHeader" }
+                ]
+            },
+            {
+                "name": "addHeapSnapshotChunk",
+                "parameters": [
+                    { "name": "uid", "type": "integer" },
+                    { "name": "chunk", "type": "string" }
+                ]
+            },
+            {
+                "name": "finishHeapSnapshot",
+                "parameters": [
+                    { "name": "uid", "type": "integer" }
+                ]
+            },
+            {
+                "name": "setRecordingProfile",
+                "parameters": [
+                    { "name": "isProfiling", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "resetProfiles"
+            },
+            {
+                "name": "reportHeapSnapshotProgress",
+                "parameters": [
+                    { "name": "done", "type": "integer" },
+                    { "name": "total", "type": "integer" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "Worker",
+        "hidden": true,
+        "types": [],
+        "commands": [
+            {
+                "name": "setWorkerInspectionEnabled",
+                "parameters": [
+                    { "name": "value", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "sendMessageToWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" },
+                    { "name": "message", "type": "object" }
+                ]
+            },
+            {
+                "name": "connectToWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" }
+                ]
+            },
+            {
+                "name": "disconnectFromWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" }
+                ]
+            },
+            {
+                "name": "setAutoconnectToWorkers",
+                "parameters": [
+                    { "name": "value", "type": "boolean" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "workerCreated",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" },
+                    { "name": "url", "type": "string" },
+                    { "name": "inspectorConnected", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "workerTerminated",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" }
+                ]
+            },
+            {
+                "name": "dispatchMessageFromWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" },
+                    { "name": "message", "type": "object" }
+                ]
+            },
+            {
+                "name": "disconnectedFromWorker"
+            }
+        ]
+    }]
+}
diff --git a/Source/devtools/OWNERS b/Source/devtools/OWNERS
new file mode 100644
index 0000000..13ebff8
--- /dev/null
+++ b/Source/devtools/OWNERS
@@ -0,0 +1,3 @@
+pfeldman@chromium.org
+vsevik@chromium.org
+yurys@chromium.org
diff --git a/Source/devtools/concatenated_devtools_audits_js.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_audits_js.target.darwin-arm.mk
new file mode 100644
index 0000000..d4a2586
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_audits_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_audits_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_audits_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_audits_js_target_concatenate_devtools_audits_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/AuditsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_audits_js
+concatenated_devtools_audits_js: third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_audits_js.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_audits_js.target.darwin-x86.mk
new file mode 100644
index 0000000..d4a2586
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_audits_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_audits_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_audits_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_audits_js_target_concatenate_devtools_audits_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/AuditsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_audits_js
+concatenated_devtools_audits_js: third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_audits_js.target.linux-arm.mk b/Source/devtools/concatenated_devtools_audits_js.target.linux-arm.mk
new file mode 100644
index 0000000..d4a2586
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_audits_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_audits_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_audits_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_audits_js_target_concatenate_devtools_audits_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/AuditsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_audits_js
+concatenated_devtools_audits_js: third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_audits_js.target.linux-x86.mk b/Source/devtools/concatenated_devtools_audits_js.target.linux-x86.mk
new file mode 100644
index 0000000..d4a2586
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_audits_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_audits_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_audits_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_audits_js_target_concatenate_devtools_audits_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/AuditsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_audits_js
+concatenated_devtools_audits_js: third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_codemirror_js.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_codemirror_js.target.darwin-arm.mk
new file mode 100644
index 0000000..2562027
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_codemirror_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_codemirror_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_codemirror_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_codemirror_js_target_concatenate_devtools_codemirror_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/CodeMirrorTextEditor.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js" true
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_codemirror_js
+concatenated_devtools_codemirror_js: third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_codemirror_js.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_codemirror_js.target.darwin-x86.mk
new file mode 100644
index 0000000..2562027
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_codemirror_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_codemirror_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_codemirror_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_codemirror_js_target_concatenate_devtools_codemirror_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/CodeMirrorTextEditor.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js" true
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_codemirror_js
+concatenated_devtools_codemirror_js: third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_codemirror_js.target.linux-arm.mk b/Source/devtools/concatenated_devtools_codemirror_js.target.linux-arm.mk
new file mode 100644
index 0000000..2562027
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_codemirror_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_codemirror_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_codemirror_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_codemirror_js_target_concatenate_devtools_codemirror_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/CodeMirrorTextEditor.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js" true
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_codemirror_js
+concatenated_devtools_codemirror_js: third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_codemirror_js.target.linux-x86.mk b/Source/devtools/concatenated_devtools_codemirror_js.target.linux-x86.mk
new file mode 100644
index 0000000..2562027
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_codemirror_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_codemirror_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_codemirror_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_codemirror_js_target_concatenate_devtools_codemirror_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/CodeMirrorTextEditor.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js" true
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_codemirror_js
+concatenated_devtools_codemirror_js: third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_css.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_css.target.darwin-arm.mk
new file mode 100644
index 0000000..46dcbea
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_css.target.darwin-arm.mk
@@ -0,0 +1,220 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_css
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp
+
+### Rules for action "concatenate_devtools_css":
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/concatenate_css_files.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AdvancedSearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Checkbox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Color.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CompilerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleMessage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsolePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviders.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieParser.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookiesTable.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSMetadata.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSStyleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Database.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DataGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Tests.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Dialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMAgent.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMCountersGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMExtension.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMPresentationUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMSyntaxHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DockController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Drawer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsTreeOutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EmptyView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAuditCategory.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionRegistryStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionServer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FontView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/GoToLineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HAREntry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HandlerRegistry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HelpScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ImageView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorBackend.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendHostStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystem.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystemManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/jsdifflib.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/KeyboardShortcut.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Linkifier.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/LiveEditSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkLog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkRequest.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkUISourceCodeProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectElementModeController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Object.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPopoverHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverridesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverviewGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Panel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ParsedURL.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Placard.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Popover.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PresentationConsoleMessageHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Progress.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProgressIndicator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RemoteObject.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Resource.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceTreeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceType.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RuntimeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SASSSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Script.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptSnippetModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SettingsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Section.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Settings.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShortcutsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShowMoreDataGridNode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarOverlay.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarTreeElement.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SimpleWorkspaceProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SnippetStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SoftContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceCSSTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceHTMLTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceJavaScriptTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMap.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Spectrum.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SplitView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StatusBarButton.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SuggestBox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TestController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextPrompt.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Toolbar.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/treeoutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIString.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserAgentSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserMetrics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/View.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ViewportControl.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkerManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Workspace.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorSyntaxHighlight.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/popover.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_concatenate_devtools_css ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/concatenate_css_files.py front_end/inspector.html front_end "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css"
+
+
+
+### Generated for copy rule.
+$(gyp_shared_intermediate_dir)/resources/inspector/auditsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/breadcrumbList.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/breakpointsList.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/buildSystemOnly.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/cmdevtools.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/codemirror.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/cssNamedFlows.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/dataGrid.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/elementsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/filteredItemSelectionDialog.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/flameChart.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/heapProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/helpScreen.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/indexedDBViews.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/inspectorCommon.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/nativeMemoryProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/navigatorView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/networkLogView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/networkPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/panelEnablerView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/profilesPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/resourceView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/resourcesPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/revisionHistory.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/scriptsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/sidebarPane.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/spectrum.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/splitView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/tabbedPane.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/textEditor.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/textPrompt.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/timelinePanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/canvasProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_copies = $(gyp_shared_intermediate_dir)/resources/inspector/auditsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/breadcrumbList.css $(gyp_shared_intermediate_dir)/resources/inspector/breakpointsList.css $(gyp_shared_intermediate_dir)/resources/inspector/buildSystemOnly.js $(gyp_shared_intermediate_dir)/resources/inspector/cmdevtools.css $(gyp_shared_intermediate_dir)/resources/inspector/codemirror.css $(gyp_shared_intermediate_dir)/resources/inspector/cssNamedFlows.css $(gyp_shared_intermediate_dir)/resources/inspector/dataGrid.css $(gyp_shared_intermediate_dir)/resources/inspector/elementsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/filteredItemSelectionDialog.css $(gyp_shared_intermediate_dir)/resources/inspector/flameChart.css $(gyp_shared_intermediate_dir)/resources/inspector/heapProfiler.css $(gyp_shared_intermediate_dir)/resources/inspector/helpScreen.css $(gyp_shared_intermediate_dir)/resources/inspector/indexedDBViews.css $(gyp_shared_intermediate_dir)/resources/inspector/inspectorCommon.css $(gyp_shared_intermediate_dir)/resources/inspector/nativeMemoryProfiler.css $(gyp_shared_intermediate_dir)/resources/inspector/navigatorView.css $(gyp_shared_intermediate_dir)/resources/inspector/networkLogView.css $(gyp_shared_intermediate_dir)/resources/inspector/networkPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/panelEnablerView.css $(gyp_shared_intermediate_dir)/resources/inspector/profilesPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/resourceView.css $(gyp_shared_intermediate_dir)/resources/inspector/resourcesPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/revisionHistory.css $(gyp_shared_intermediate_dir)/resources/inspector/scriptsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/sidebarPane.css $(gyp_shared_intermediate_dir)/resources/inspector/spectrum.css $(gyp_shared_intermediate_dir)/resources/inspector/splitView.css $(gyp_shared_intermediate_dir)/resources/inspector/tabbedPane.css $(gyp_shared_intermediate_dir)/resources/inspector/textEditor.css $(gyp_shared_intermediate_dir)/resources/inspector/textPrompt.css $(gyp_shared_intermediate_dir)/resources/inspector/timelinePanel.css $(gyp_shared_intermediate_dir)/resources/inspector/canvasProfiler.css
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css \
+	$(third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_copies)
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_css
+concatenated_devtools_css: third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_css.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_css.target.darwin-x86.mk
new file mode 100644
index 0000000..46dcbea
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_css.target.darwin-x86.mk
@@ -0,0 +1,220 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_css
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp
+
+### Rules for action "concatenate_devtools_css":
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/concatenate_css_files.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AdvancedSearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Checkbox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Color.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CompilerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleMessage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsolePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviders.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieParser.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookiesTable.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSMetadata.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSStyleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Database.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DataGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Tests.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Dialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMAgent.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMCountersGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMExtension.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMPresentationUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMSyntaxHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DockController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Drawer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsTreeOutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EmptyView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAuditCategory.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionRegistryStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionServer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FontView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/GoToLineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HAREntry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HandlerRegistry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HelpScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ImageView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorBackend.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendHostStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystem.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystemManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/jsdifflib.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/KeyboardShortcut.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Linkifier.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/LiveEditSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkLog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkRequest.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkUISourceCodeProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectElementModeController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Object.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPopoverHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverridesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverviewGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Panel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ParsedURL.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Placard.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Popover.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PresentationConsoleMessageHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Progress.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProgressIndicator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RemoteObject.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Resource.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceTreeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceType.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RuntimeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SASSSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Script.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptSnippetModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SettingsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Section.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Settings.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShortcutsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShowMoreDataGridNode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarOverlay.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarTreeElement.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SimpleWorkspaceProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SnippetStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SoftContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceCSSTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceHTMLTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceJavaScriptTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMap.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Spectrum.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SplitView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StatusBarButton.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SuggestBox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TestController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextPrompt.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Toolbar.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/treeoutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIString.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserAgentSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserMetrics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/View.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ViewportControl.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkerManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Workspace.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorSyntaxHighlight.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/popover.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_concatenate_devtools_css ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/concatenate_css_files.py front_end/inspector.html front_end "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css"
+
+
+
+### Generated for copy rule.
+$(gyp_shared_intermediate_dir)/resources/inspector/auditsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/breadcrumbList.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/breakpointsList.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/buildSystemOnly.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/cmdevtools.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/codemirror.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/cssNamedFlows.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/dataGrid.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/elementsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/filteredItemSelectionDialog.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/flameChart.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/heapProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/helpScreen.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/indexedDBViews.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/inspectorCommon.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/nativeMemoryProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/navigatorView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/networkLogView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/networkPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/panelEnablerView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/profilesPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/resourceView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/resourcesPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/revisionHistory.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/scriptsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/sidebarPane.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/spectrum.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/splitView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/tabbedPane.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/textEditor.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/textPrompt.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/timelinePanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/canvasProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_copies = $(gyp_shared_intermediate_dir)/resources/inspector/auditsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/breadcrumbList.css $(gyp_shared_intermediate_dir)/resources/inspector/breakpointsList.css $(gyp_shared_intermediate_dir)/resources/inspector/buildSystemOnly.js $(gyp_shared_intermediate_dir)/resources/inspector/cmdevtools.css $(gyp_shared_intermediate_dir)/resources/inspector/codemirror.css $(gyp_shared_intermediate_dir)/resources/inspector/cssNamedFlows.css $(gyp_shared_intermediate_dir)/resources/inspector/dataGrid.css $(gyp_shared_intermediate_dir)/resources/inspector/elementsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/filteredItemSelectionDialog.css $(gyp_shared_intermediate_dir)/resources/inspector/flameChart.css $(gyp_shared_intermediate_dir)/resources/inspector/heapProfiler.css $(gyp_shared_intermediate_dir)/resources/inspector/helpScreen.css $(gyp_shared_intermediate_dir)/resources/inspector/indexedDBViews.css $(gyp_shared_intermediate_dir)/resources/inspector/inspectorCommon.css $(gyp_shared_intermediate_dir)/resources/inspector/nativeMemoryProfiler.css $(gyp_shared_intermediate_dir)/resources/inspector/navigatorView.css $(gyp_shared_intermediate_dir)/resources/inspector/networkLogView.css $(gyp_shared_intermediate_dir)/resources/inspector/networkPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/panelEnablerView.css $(gyp_shared_intermediate_dir)/resources/inspector/profilesPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/resourceView.css $(gyp_shared_intermediate_dir)/resources/inspector/resourcesPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/revisionHistory.css $(gyp_shared_intermediate_dir)/resources/inspector/scriptsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/sidebarPane.css $(gyp_shared_intermediate_dir)/resources/inspector/spectrum.css $(gyp_shared_intermediate_dir)/resources/inspector/splitView.css $(gyp_shared_intermediate_dir)/resources/inspector/tabbedPane.css $(gyp_shared_intermediate_dir)/resources/inspector/textEditor.css $(gyp_shared_intermediate_dir)/resources/inspector/textPrompt.css $(gyp_shared_intermediate_dir)/resources/inspector/timelinePanel.css $(gyp_shared_intermediate_dir)/resources/inspector/canvasProfiler.css
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css \
+	$(third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_copies)
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_css
+concatenated_devtools_css: third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_css.target.linux-arm.mk b/Source/devtools/concatenated_devtools_css.target.linux-arm.mk
new file mode 100644
index 0000000..46dcbea
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_css.target.linux-arm.mk
@@ -0,0 +1,220 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_css
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp
+
+### Rules for action "concatenate_devtools_css":
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/concatenate_css_files.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AdvancedSearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Checkbox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Color.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CompilerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleMessage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsolePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviders.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieParser.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookiesTable.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSMetadata.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSStyleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Database.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DataGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Tests.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Dialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMAgent.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMCountersGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMExtension.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMPresentationUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMSyntaxHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DockController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Drawer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsTreeOutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EmptyView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAuditCategory.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionRegistryStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionServer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FontView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/GoToLineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HAREntry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HandlerRegistry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HelpScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ImageView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorBackend.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendHostStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystem.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystemManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/jsdifflib.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/KeyboardShortcut.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Linkifier.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/LiveEditSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkLog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkRequest.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkUISourceCodeProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectElementModeController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Object.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPopoverHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverridesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverviewGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Panel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ParsedURL.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Placard.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Popover.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PresentationConsoleMessageHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Progress.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProgressIndicator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RemoteObject.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Resource.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceTreeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceType.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RuntimeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SASSSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Script.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptSnippetModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SettingsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Section.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Settings.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShortcutsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShowMoreDataGridNode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarOverlay.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarTreeElement.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SimpleWorkspaceProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SnippetStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SoftContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceCSSTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceHTMLTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceJavaScriptTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMap.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Spectrum.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SplitView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StatusBarButton.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SuggestBox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TestController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextPrompt.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Toolbar.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/treeoutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIString.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserAgentSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserMetrics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/View.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ViewportControl.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkerManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Workspace.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorSyntaxHighlight.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/popover.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_concatenate_devtools_css ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/concatenate_css_files.py front_end/inspector.html front_end "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css"
+
+
+
+### Generated for copy rule.
+$(gyp_shared_intermediate_dir)/resources/inspector/auditsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/breadcrumbList.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/breakpointsList.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/buildSystemOnly.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/cmdevtools.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/codemirror.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/cssNamedFlows.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/dataGrid.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/elementsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/filteredItemSelectionDialog.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/flameChart.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/heapProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/helpScreen.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/indexedDBViews.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/inspectorCommon.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/nativeMemoryProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/navigatorView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/networkLogView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/networkPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/panelEnablerView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/profilesPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/resourceView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/resourcesPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/revisionHistory.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/scriptsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/sidebarPane.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/spectrum.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/splitView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/tabbedPane.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/textEditor.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/textPrompt.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/timelinePanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/canvasProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_copies = $(gyp_shared_intermediate_dir)/resources/inspector/auditsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/breadcrumbList.css $(gyp_shared_intermediate_dir)/resources/inspector/breakpointsList.css $(gyp_shared_intermediate_dir)/resources/inspector/buildSystemOnly.js $(gyp_shared_intermediate_dir)/resources/inspector/cmdevtools.css $(gyp_shared_intermediate_dir)/resources/inspector/codemirror.css $(gyp_shared_intermediate_dir)/resources/inspector/cssNamedFlows.css $(gyp_shared_intermediate_dir)/resources/inspector/dataGrid.css $(gyp_shared_intermediate_dir)/resources/inspector/elementsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/filteredItemSelectionDialog.css $(gyp_shared_intermediate_dir)/resources/inspector/flameChart.css $(gyp_shared_intermediate_dir)/resources/inspector/heapProfiler.css $(gyp_shared_intermediate_dir)/resources/inspector/helpScreen.css $(gyp_shared_intermediate_dir)/resources/inspector/indexedDBViews.css $(gyp_shared_intermediate_dir)/resources/inspector/inspectorCommon.css $(gyp_shared_intermediate_dir)/resources/inspector/nativeMemoryProfiler.css $(gyp_shared_intermediate_dir)/resources/inspector/navigatorView.css $(gyp_shared_intermediate_dir)/resources/inspector/networkLogView.css $(gyp_shared_intermediate_dir)/resources/inspector/networkPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/panelEnablerView.css $(gyp_shared_intermediate_dir)/resources/inspector/profilesPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/resourceView.css $(gyp_shared_intermediate_dir)/resources/inspector/resourcesPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/revisionHistory.css $(gyp_shared_intermediate_dir)/resources/inspector/scriptsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/sidebarPane.css $(gyp_shared_intermediate_dir)/resources/inspector/spectrum.css $(gyp_shared_intermediate_dir)/resources/inspector/splitView.css $(gyp_shared_intermediate_dir)/resources/inspector/tabbedPane.css $(gyp_shared_intermediate_dir)/resources/inspector/textEditor.css $(gyp_shared_intermediate_dir)/resources/inspector/textPrompt.css $(gyp_shared_intermediate_dir)/resources/inspector/timelinePanel.css $(gyp_shared_intermediate_dir)/resources/inspector/canvasProfiler.css
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css \
+	$(third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_copies)
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_css
+concatenated_devtools_css: third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_css.target.linux-x86.mk b/Source/devtools/concatenated_devtools_css.target.linux-x86.mk
new file mode 100644
index 0000000..46dcbea
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_css.target.linux-x86.mk
@@ -0,0 +1,220 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_css
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp
+
+### Rules for action "concatenate_devtools_css":
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/concatenate_css_files.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AdvancedSearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Checkbox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Color.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CompilerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleMessage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsolePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviders.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieParser.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookiesTable.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSMetadata.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSStyleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Database.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DataGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Tests.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Dialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMAgent.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMCountersGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMExtension.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMPresentationUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMSyntaxHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DockController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Drawer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsTreeOutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EmptyView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAuditCategory.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionRegistryStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionServer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FontView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/GoToLineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HAREntry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HandlerRegistry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HelpScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ImageView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorBackend.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendHostStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystem.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystemManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/jsdifflib.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/KeyboardShortcut.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Linkifier.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/LiveEditSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkLog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkRequest.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkUISourceCodeProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectElementModeController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Object.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPopoverHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverridesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverviewGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Panel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ParsedURL.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Placard.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Popover.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PresentationConsoleMessageHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Progress.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProgressIndicator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RemoteObject.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Resource.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceTreeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceType.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RuntimeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SASSSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Script.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptSnippetModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SettingsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Section.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Settings.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShortcutsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShowMoreDataGridNode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarOverlay.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarTreeElement.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SimpleWorkspaceProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SnippetStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SoftContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceCSSTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceHTMLTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceJavaScriptTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMap.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Spectrum.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SplitView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StatusBarButton.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SuggestBox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TestController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextPrompt.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Toolbar.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/treeoutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIString.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserAgentSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserMetrics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/View.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ViewportControl.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkerManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Workspace.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorSyntaxHighlight.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/popover.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_concatenate_devtools_css ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/concatenate_css_files.py front_end/inspector.html front_end "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css"
+
+
+
+### Generated for copy rule.
+$(gyp_shared_intermediate_dir)/resources/inspector/auditsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/breadcrumbList.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/breakpointsList.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/buildSystemOnly.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/cmdevtools.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/codemirror.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/cssNamedFlows.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/dataGrid.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/elementsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/filteredItemSelectionDialog.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/flameChart.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/heapProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/helpScreen.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/indexedDBViews.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/inspectorCommon.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/nativeMemoryProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/navigatorView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/networkLogView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/networkPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/panelEnablerView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/profilesPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/resourceView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/resourcesPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/revisionHistory.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/scriptsPanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/sidebarPane.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/spectrum.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/splitView.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/tabbedPane.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/textEditor.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/textPrompt.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/timelinePanel.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+$(gyp_shared_intermediate_dir)/resources/inspector/canvasProfiler.css: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(GYP_TARGET_DEPENDENCIES) | $(ACP)
+	@echo Copying: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) -r $< $@
+
+third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_copies = $(gyp_shared_intermediate_dir)/resources/inspector/auditsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/breadcrumbList.css $(gyp_shared_intermediate_dir)/resources/inspector/breakpointsList.css $(gyp_shared_intermediate_dir)/resources/inspector/buildSystemOnly.js $(gyp_shared_intermediate_dir)/resources/inspector/cmdevtools.css $(gyp_shared_intermediate_dir)/resources/inspector/codemirror.css $(gyp_shared_intermediate_dir)/resources/inspector/cssNamedFlows.css $(gyp_shared_intermediate_dir)/resources/inspector/dataGrid.css $(gyp_shared_intermediate_dir)/resources/inspector/elementsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/filteredItemSelectionDialog.css $(gyp_shared_intermediate_dir)/resources/inspector/flameChart.css $(gyp_shared_intermediate_dir)/resources/inspector/heapProfiler.css $(gyp_shared_intermediate_dir)/resources/inspector/helpScreen.css $(gyp_shared_intermediate_dir)/resources/inspector/indexedDBViews.css $(gyp_shared_intermediate_dir)/resources/inspector/inspectorCommon.css $(gyp_shared_intermediate_dir)/resources/inspector/nativeMemoryProfiler.css $(gyp_shared_intermediate_dir)/resources/inspector/navigatorView.css $(gyp_shared_intermediate_dir)/resources/inspector/networkLogView.css $(gyp_shared_intermediate_dir)/resources/inspector/networkPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/panelEnablerView.css $(gyp_shared_intermediate_dir)/resources/inspector/profilesPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/resourceView.css $(gyp_shared_intermediate_dir)/resources/inspector/resourcesPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/revisionHistory.css $(gyp_shared_intermediate_dir)/resources/inspector/scriptsPanel.css $(gyp_shared_intermediate_dir)/resources/inspector/sidebarPane.css $(gyp_shared_intermediate_dir)/resources/inspector/spectrum.css $(gyp_shared_intermediate_dir)/resources/inspector/splitView.css $(gyp_shared_intermediate_dir)/resources/inspector/tabbedPane.css $(gyp_shared_intermediate_dir)/resources/inspector/textEditor.css $(gyp_shared_intermediate_dir)/resources/inspector/textPrompt.css $(gyp_shared_intermediate_dir)/resources/inspector/timelinePanel.css $(gyp_shared_intermediate_dir)/resources/inspector/canvasProfiler.css
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css \
+	$(third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_css_target_copies)
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_css
+concatenated_devtools_css: third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_elements_js.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_elements_js.target.darwin-arm.mk
new file mode 100644
index 0000000..c2639ef
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_elements_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_elements_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_elements_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_elements_js_target_concatenate_devtools_elements_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ElementsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_elements_js
+concatenated_devtools_elements_js: third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_elements_js.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_elements_js.target.darwin-x86.mk
new file mode 100644
index 0000000..c2639ef
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_elements_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_elements_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_elements_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_elements_js_target_concatenate_devtools_elements_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ElementsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_elements_js
+concatenated_devtools_elements_js: third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_elements_js.target.linux-arm.mk b/Source/devtools/concatenated_devtools_elements_js.target.linux-arm.mk
new file mode 100644
index 0000000..c2639ef
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_elements_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_elements_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_elements_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_elements_js_target_concatenate_devtools_elements_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ElementsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_elements_js
+concatenated_devtools_elements_js: third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_elements_js.target.linux-x86.mk b/Source/devtools/concatenated_devtools_elements_js.target.linux-x86.mk
new file mode 100644
index 0000000..c2639ef
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_elements_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_elements_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_elements_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_elements_js_target_concatenate_devtools_elements_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ElementsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_elements_js
+concatenated_devtools_elements_js: third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_js.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_js.target.darwin-arm.mk
new file mode 100644
index 0000000..b2f68f2
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_js.target.darwin-arm.mk
@@ -0,0 +1,52 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp)/frontend_protocol_sources.stamp
+
+### Rules for action "concatenate_devtools_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/concatenate_js_files.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AdvancedSearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Checkbox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Color.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CompilerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleMessage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsolePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviders.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieParser.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookiesTable.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSMetadata.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSStyleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Database.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DataGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Tests.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Dialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMAgent.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMCountersGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMExtension.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMPresentationUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMSyntaxHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DockController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Drawer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsTreeOutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EmptyView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAuditCategory.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionRegistryStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionServer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FontView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/GoToLineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HAREntry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HandlerRegistry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HelpScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ImageView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorBackend.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendHostStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystem.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystemManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/jsdifflib.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/KeyboardShortcut.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Linkifier.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/LiveEditSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkLog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkRequest.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkUISourceCodeProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectElementModeController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Object.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPopoverHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverridesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverviewGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Panel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ParsedURL.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Placard.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Popover.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PresentationConsoleMessageHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Progress.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProgressIndicator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RemoteObject.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Resource.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceTreeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceType.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RuntimeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SASSSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Script.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptSnippetModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SettingsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Section.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Settings.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShortcutsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShowMoreDataGridNode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarOverlay.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarTreeElement.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SimpleWorkspaceProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SnippetStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SoftContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceCSSTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceHTMLTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceJavaScriptTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMap.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Spectrum.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SplitView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StatusBarButton.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SuggestBox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TestController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextPrompt.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Toolbar.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/treeoutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIString.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserAgentSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserMetrics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/View.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ViewportControl.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkerManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Workspace.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorSyntaxHighlight.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/popover.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_js_target_concatenate_devtools_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/concatenate_js_files.py front_end/inspector.html front_end "$(gyp_shared_intermediate_dir)/webcore" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_js
+concatenated_devtools_js: third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_js.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_js.target.darwin-x86.mk
new file mode 100644
index 0000000..b2f68f2
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_js.target.darwin-x86.mk
@@ -0,0 +1,52 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp)/frontend_protocol_sources.stamp
+
+### Rules for action "concatenate_devtools_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/concatenate_js_files.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AdvancedSearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Checkbox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Color.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CompilerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleMessage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsolePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviders.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieParser.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookiesTable.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSMetadata.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSStyleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Database.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DataGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Tests.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Dialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMAgent.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMCountersGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMExtension.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMPresentationUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMSyntaxHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DockController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Drawer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsTreeOutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EmptyView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAuditCategory.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionRegistryStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionServer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FontView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/GoToLineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HAREntry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HandlerRegistry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HelpScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ImageView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorBackend.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendHostStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystem.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystemManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/jsdifflib.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/KeyboardShortcut.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Linkifier.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/LiveEditSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkLog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkRequest.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkUISourceCodeProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectElementModeController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Object.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPopoverHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverridesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverviewGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Panel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ParsedURL.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Placard.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Popover.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PresentationConsoleMessageHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Progress.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProgressIndicator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RemoteObject.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Resource.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceTreeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceType.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RuntimeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SASSSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Script.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptSnippetModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SettingsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Section.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Settings.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShortcutsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShowMoreDataGridNode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarOverlay.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarTreeElement.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SimpleWorkspaceProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SnippetStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SoftContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceCSSTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceHTMLTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceJavaScriptTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMap.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Spectrum.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SplitView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StatusBarButton.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SuggestBox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TestController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextPrompt.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Toolbar.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/treeoutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIString.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserAgentSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserMetrics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/View.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ViewportControl.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkerManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Workspace.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorSyntaxHighlight.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/popover.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_js_target_concatenate_devtools_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/concatenate_js_files.py front_end/inspector.html front_end "$(gyp_shared_intermediate_dir)/webcore" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_js
+concatenated_devtools_js: third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_js.target.linux-arm.mk b/Source/devtools/concatenated_devtools_js.target.linux-arm.mk
new file mode 100644
index 0000000..b2f68f2
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_js.target.linux-arm.mk
@@ -0,0 +1,52 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp)/frontend_protocol_sources.stamp
+
+### Rules for action "concatenate_devtools_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/concatenate_js_files.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AdvancedSearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Checkbox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Color.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CompilerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleMessage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsolePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviders.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieParser.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookiesTable.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSMetadata.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSStyleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Database.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DataGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Tests.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Dialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMAgent.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMCountersGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMExtension.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMPresentationUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMSyntaxHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DockController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Drawer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsTreeOutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EmptyView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAuditCategory.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionRegistryStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionServer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FontView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/GoToLineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HAREntry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HandlerRegistry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HelpScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ImageView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorBackend.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendHostStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystem.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystemManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/jsdifflib.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/KeyboardShortcut.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Linkifier.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/LiveEditSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkLog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkRequest.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkUISourceCodeProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectElementModeController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Object.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPopoverHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverridesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverviewGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Panel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ParsedURL.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Placard.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Popover.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PresentationConsoleMessageHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Progress.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProgressIndicator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RemoteObject.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Resource.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceTreeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceType.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RuntimeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SASSSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Script.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptSnippetModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SettingsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Section.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Settings.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShortcutsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShowMoreDataGridNode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarOverlay.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarTreeElement.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SimpleWorkspaceProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SnippetStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SoftContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceCSSTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceHTMLTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceJavaScriptTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMap.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Spectrum.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SplitView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StatusBarButton.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SuggestBox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TestController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextPrompt.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Toolbar.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/treeoutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIString.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserAgentSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserMetrics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/View.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ViewportControl.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkerManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Workspace.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorSyntaxHighlight.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/popover.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_js_target_concatenate_devtools_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/concatenate_js_files.py front_end/inspector.html front_end "$(gyp_shared_intermediate_dir)/webcore" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_js
+concatenated_devtools_js: third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_js.target.linux-x86.mk b/Source/devtools/concatenated_devtools_js.target.linux-x86.mk
new file mode 100644
index 0000000..b2f68f2
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_js.target.linux-x86.mk
@@ -0,0 +1,52 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp)/frontend_protocol_sources.stamp
+
+### Rules for action "concatenate_devtools_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/concatenate_js_files.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AdvancedSearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Checkbox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Color.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CompilerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleMessage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsolePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ConsoleView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContentProviders.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookieParser.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CookiesTable.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSMetadata.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSStyleModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Database.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DataGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DebuggerScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Tests.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Dialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMAgent.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMCountersGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMExtension.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMPresentationUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMSyntaxHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DefaultScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DockController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Drawer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsTreeOutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EmptyView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAuditCategory.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionRegistryStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionServer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemProjectDelegate.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FontView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/GoToLineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HAREntry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HandlerRegistry.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HelpScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ImageView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorBackend.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorFrontendHostStub.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystem.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IsolatedFileSystemManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/jsdifflib.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/KeyboardShortcut.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Linkifier.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/LiveEditSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeBreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkLog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkRequest.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkUISourceCodeProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/InspectElementModeController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Object.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPopoverHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ObjectPropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverridesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/OverviewGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Panel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ParsedURL.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Placard.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Popover.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PresentationConsoleMessageHelper.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Progress.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProgressIndicator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RemoteObject.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Resource.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceScriptMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceTreeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceType.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RuntimeModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SASSSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Script.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptSnippetModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SearchController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SettingsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Section.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Settings.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShortcutsScreen.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ShowMoreDataGridNode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarOverlay.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SidebarTreeElement.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SimpleWorkspaceProvider.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SnippetStorage.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SoftContextMenu.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceCSSTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceHTMLTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceJavaScriptTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMap.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SourceTokenizer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Spectrum.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SplitView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StatusBarButton.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSourceMapping.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/SuggestBox.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TestController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorHighlighter.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextEditorModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextPrompt.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TextUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineGrid.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanelDescriptor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Toolbar.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/treeoutline.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCode.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIString.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UIUtils.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserAgentSupport.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UserMetrics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/View.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ViewportControl.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkerManager.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Workspace.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorSyntaxHighlight.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/popover.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowCollectionsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSNamedFlowView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ElementsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/EventListenersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MetricsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/PropertiesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StylesSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditCategories.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditFormatters.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditResultView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditRules.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/AuditsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CodeMirrorTextEditor.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/css.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/closebrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/matchbrackets.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/javascript.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/xml.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/markselection.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_js_target_concatenate_devtools_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/concatenate_js_files.py front_end/inspector.html front_end "$(gyp_shared_intermediate_dir)/webcore" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_js
+concatenated_devtools_js: third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_network_js.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_network_js.target.darwin-arm.mk
new file mode 100644
index 0000000..8a9441d
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_network_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_network_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_network_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_network_js_target_concatenate_devtools_network_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/NetworkPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_network_js
+concatenated_devtools_network_js: third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_network_js.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_network_js.target.darwin-x86.mk
new file mode 100644
index 0000000..8a9441d
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_network_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_network_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_network_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_network_js_target_concatenate_devtools_network_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/NetworkPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_network_js
+concatenated_devtools_network_js: third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_network_js.target.linux-arm.mk b/Source/devtools/concatenated_devtools_network_js.target.linux-arm.mk
new file mode 100644
index 0000000..8a9441d
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_network_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_network_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_network_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_network_js_target_concatenate_devtools_network_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/NetworkPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_network_js
+concatenated_devtools_network_js: third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_network_js.target.linux-x86.mk b/Source/devtools/concatenated_devtools_network_js.target.linux-x86.mk
new file mode 100644
index 0000000..8a9441d
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_network_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_network_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_network_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkItemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestCookiesView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHeadersView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestHTMLView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestJSONView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestPreviewView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestResponseView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestTimingView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RequestView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourceWebSocketFrameView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NetworkPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_network_js_target_concatenate_devtools_network_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/NetworkPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_network_js
+concatenated_devtools_network_js: third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_profiles_js.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_profiles_js.target.darwin-arm.mk
new file mode 100644
index 0000000..fb4d05e
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_profiles_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_profiles_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_profiles_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_profiles_js_target_concatenate_devtools_profiles_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ProfilesPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_profiles_js
+concatenated_devtools_profiles_js: third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_profiles_js.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_profiles_js.target.darwin-x86.mk
new file mode 100644
index 0000000..fb4d05e
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_profiles_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_profiles_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_profiles_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_profiles_js_target_concatenate_devtools_profiles_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ProfilesPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_profiles_js
+concatenated_devtools_profiles_js: third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_profiles_js.target.linux-arm.mk b/Source/devtools/concatenated_devtools_profiles_js.target.linux-arm.mk
new file mode 100644
index 0000000..fb4d05e
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_profiles_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_profiles_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_profiles_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_profiles_js_target_concatenate_devtools_profiles_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ProfilesPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_profiles_js
+concatenated_devtools_profiles_js: third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_profiles_js.target.linux-x86.mk b/Source/devtools/concatenated_devtools_profiles_js.target.linux-x86.mk
new file mode 100644
index 0000000..fb4d05e
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_profiles_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_profiles_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_profiles_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BottomUpProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CPUProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CSSSelectorProfileView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FlameChart.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotDataGrids.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotGridNodes.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotProxy.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemorySnapshotView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfilesPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ProfileLauncherView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TopDownProfileDataGridTree.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CanvasProfileView.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_profiles_js_target_concatenate_devtools_profiles_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ProfilesPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_profiles_js
+concatenated_devtools_profiles_js: third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_resources_js.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_resources_js.target.darwin-arm.mk
new file mode 100644
index 0000000..0fdabef
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_resources_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_resources_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_resources_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_resources_js_target_concatenate_devtools_resources_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ResourcesPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_resources_js
+concatenated_devtools_resources_js: third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_resources_js.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_resources_js.target.darwin-x86.mk
new file mode 100644
index 0000000..0fdabef
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_resources_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_resources_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_resources_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_resources_js_target_concatenate_devtools_resources_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ResourcesPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_resources_js
+concatenated_devtools_resources_js: third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_resources_js.target.linux-arm.mk b/Source/devtools/concatenated_devtools_resources_js.target.linux-arm.mk
new file mode 100644
index 0000000..0fdabef
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_resources_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_resources_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_resources_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_resources_js_target_concatenate_devtools_resources_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ResourcesPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_resources_js
+concatenated_devtools_resources_js: third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_resources_js.target.linux-x86.mk b/Source/devtools/concatenated_devtools_resources_js.target.linux-x86.mk
new file mode 100644
index 0000000..0fdabef
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_resources_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_resources_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_resources_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ApplicationCacheItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DOMStorageItemsView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseQueryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DatabaseTableView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DirectoryContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileContentView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FileSystemView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/IndexedDBViews.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ResourcesPanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_resources_js_target_concatenate_devtools_resources_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ResourcesPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_resources_js
+concatenated_devtools_resources_js: third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_scripts_js.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_scripts_js.target.darwin-arm.mk
new file mode 100644
index 0000000..32da264
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_scripts_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_scripts_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_scripts_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_scripts_js_target_concatenate_devtools_scripts_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ScriptsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_scripts_js
+concatenated_devtools_scripts_js: third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_scripts_js.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_scripts_js.target.darwin-x86.mk
new file mode 100644
index 0000000..32da264
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_scripts_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_scripts_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_scripts_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_scripts_js_target_concatenate_devtools_scripts_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ScriptsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_scripts_js
+concatenated_devtools_scripts_js: third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_scripts_js.target.linux-arm.mk b/Source/devtools/concatenated_devtools_scripts_js.target.linux-arm.mk
new file mode 100644
index 0000000..32da264
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_scripts_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_scripts_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_scripts_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_scripts_js_target_concatenate_devtools_scripts_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ScriptsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_scripts_js
+concatenated_devtools_scripts_js: third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_scripts_js.target.linux-x86.mk b/Source/devtools/concatenated_devtools_scripts_js.target.linux-x86.mk
new file mode 100644
index 0000000..32da264
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_scripts_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_scripts_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_scripts_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/BreakpointsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/CallStackSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/FilteredItemSelectionDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JavaScriptSourceFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorOverlayController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NavigatorView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/RevisionHistoryView.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScopeChainSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsNavigator.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsPanel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptsSearchScope.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/StyleSheetOutlineDialog.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TabbedEditorContainer.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UISourceCodeFrame.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WatchExpressionsSidebarPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/WorkersSidebarPane.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_scripts_js_target_concatenate_devtools_scripts_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ScriptsPanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_scripts_js
+concatenated_devtools_scripts_js: third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_timeline_js.target.darwin-arm.mk b/Source/devtools/concatenated_devtools_timeline_js.target.darwin-arm.mk
new file mode 100644
index 0000000..d1870a1
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_timeline_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_timeline_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_timeline_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_timeline_js_target_concatenate_devtools_timeline_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/TimelinePanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_timeline_js
+concatenated_devtools_timeline_js: third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_timeline_js.target.darwin-x86.mk b/Source/devtools/concatenated_devtools_timeline_js.target.darwin-x86.mk
new file mode 100644
index 0000000..d1870a1
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_timeline_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_timeline_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_timeline_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_timeline_js_target_concatenate_devtools_timeline_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/TimelinePanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_timeline_js
+concatenated_devtools_timeline_js: third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_timeline_js.target.linux-arm.mk b/Source/devtools/concatenated_devtools_timeline_js.target.linux-arm.mk
new file mode 100644
index 0000000..d1870a1
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_timeline_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_timeline_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_timeline_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_timeline_js_target_concatenate_devtools_timeline_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/TimelinePanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_timeline_js
+concatenated_devtools_timeline_js: third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_devtools_timeline_js.target.linux-x86.mk b/Source/devtools/concatenated_devtools_timeline_js.target.linux-x86.mk
new file mode 100644
index 0000000..d1870a1
--- /dev/null
+++ b/Source/devtools/concatenated_devtools_timeline_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+LOCAL_MODULE_STEM := concatenated_devtools_timeline_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_devtools_timeline_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/MemoryStatistics.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/NativeMemoryGraph.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineFrameController.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePresentationModel.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelineOverviewPane.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/TimelinePanel.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_devtools_timeline_js_target_concatenate_devtools_timeline_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/TimelinePanel.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_devtools_timeline_js
+concatenated_devtools_timeline_js: third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_heap_snapshot_worker_js.target.darwin-arm.mk b/Source/devtools/concatenated_heap_snapshot_worker_js.target.darwin-arm.mk
new file mode 100644
index 0000000..c93b27b
--- /dev/null
+++ b/Source/devtools/concatenated_heap_snapshot_worker_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+LOCAL_MODULE_STEM := concatenated_heap_snapshot_worker_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_heap_snapshot_worker_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_heap_snapshot_worker_js_target_concatenate_heap_snapshot_worker_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/HeapSnapshotWorker.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_heap_snapshot_worker_js
+concatenated_heap_snapshot_worker_js: third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_heap_snapshot_worker_js.target.darwin-x86.mk b/Source/devtools/concatenated_heap_snapshot_worker_js.target.darwin-x86.mk
new file mode 100644
index 0000000..c93b27b
--- /dev/null
+++ b/Source/devtools/concatenated_heap_snapshot_worker_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+LOCAL_MODULE_STEM := concatenated_heap_snapshot_worker_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_heap_snapshot_worker_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_heap_snapshot_worker_js_target_concatenate_heap_snapshot_worker_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/HeapSnapshotWorker.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_heap_snapshot_worker_js
+concatenated_heap_snapshot_worker_js: third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_heap_snapshot_worker_js.target.linux-arm.mk b/Source/devtools/concatenated_heap_snapshot_worker_js.target.linux-arm.mk
new file mode 100644
index 0000000..c93b27b
--- /dev/null
+++ b/Source/devtools/concatenated_heap_snapshot_worker_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+LOCAL_MODULE_STEM := concatenated_heap_snapshot_worker_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_heap_snapshot_worker_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_heap_snapshot_worker_js_target_concatenate_heap_snapshot_worker_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/HeapSnapshotWorker.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_heap_snapshot_worker_js
+concatenated_heap_snapshot_worker_js: third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_heap_snapshot_worker_js.target.linux-x86.mk b/Source/devtools/concatenated_heap_snapshot_worker_js.target.linux-x86.mk
new file mode 100644
index 0000000..c93b27b
--- /dev/null
+++ b/Source/devtools/concatenated_heap_snapshot_worker_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+LOCAL_MODULE_STEM := concatenated_heap_snapshot_worker_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_heap_snapshot_worker_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotLoader.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/JSHeapSnapshot.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/utilities.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_heap_snapshot_worker_js_target_concatenate_heap_snapshot_worker_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/HeapSnapshotWorker.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_heap_snapshot_worker_js
+concatenated_heap_snapshot_worker_js: third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_script_formatter_worker_js.target.darwin-arm.mk b/Source/devtools/concatenated_script_formatter_worker_js.target.darwin-arm.mk
new file mode 100644
index 0000000..8eda670
--- /dev/null
+++ b/Source/devtools/concatenated_script_formatter_worker_js.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+LOCAL_MODULE_STEM := concatenated_script_formatter_worker_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_script_formatter_worker_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UglifyJS/parse-js.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_script_formatter_worker_js_target_concatenate_script_formatter_worker_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ScriptFormatterWorker.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_script_formatter_worker_js
+concatenated_script_formatter_worker_js: third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_script_formatter_worker_js.target.darwin-x86.mk b/Source/devtools/concatenated_script_formatter_worker_js.target.darwin-x86.mk
new file mode 100644
index 0000000..8eda670
--- /dev/null
+++ b/Source/devtools/concatenated_script_formatter_worker_js.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+LOCAL_MODULE_STEM := concatenated_script_formatter_worker_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_script_formatter_worker_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UglifyJS/parse-js.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_script_formatter_worker_js_target_concatenate_script_formatter_worker_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ScriptFormatterWorker.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_script_formatter_worker_js
+concatenated_script_formatter_worker_js: third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_script_formatter_worker_js.target.linux-arm.mk b/Source/devtools/concatenated_script_formatter_worker_js.target.linux-arm.mk
new file mode 100644
index 0000000..8eda670
--- /dev/null
+++ b/Source/devtools/concatenated_script_formatter_worker_js.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+LOCAL_MODULE_STEM := concatenated_script_formatter_worker_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_script_formatter_worker_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UglifyJS/parse-js.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_script_formatter_worker_js_target_concatenate_script_formatter_worker_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ScriptFormatterWorker.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_script_formatter_worker_js
+concatenated_script_formatter_worker_js: third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/concatenated_script_formatter_worker_js.target.linux-x86.mk b/Source/devtools/concatenated_script_formatter_worker_js.target.linux-x86.mk
new file mode 100644
index 0000000..8eda670
--- /dev/null
+++ b/Source/devtools/concatenated_script_formatter_worker_js.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+LOCAL_MODULE_STEM := concatenated_script_formatter_worker_js
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "concatenate_script_formatter_worker_js":
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/inline_js_imports.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ScriptFormatterWorker.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/UglifyJS/parse-js.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_concatenated_script_formatter_worker_js_target_concatenate_script_formatter_worker_js ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/inline_js_imports.py front_end/ScriptFormatterWorker.js front_end "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+
+# Alias gyp target name.
+.PHONY: concatenated_script_formatter_worker_js
+concatenated_script_formatter_worker_js: third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/devtools.gyp b/Source/devtools/devtools.gyp
new file mode 100644
index 0000000..a7a8c00
--- /dev/null
+++ b/Source/devtools/devtools.gyp
@@ -0,0 +1,882 @@
+#
+# Copyright (C) 2013 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:
+#
+#         * 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.
+#
+
+{
+    'variables': {
+        # If debug_devtools is set to 1, JavaScript files for DevTools are
+        # stored as is. Otherwise, a concatenated file is stored.
+        'debug_devtools%': 0,
+        'devtools_files': [
+            'front_end/inspector.html',
+            'front_end/AdvancedSearchController.js',
+            'front_end/ApplicationCacheModel.js',
+            'front_end/BreakpointManager.js',
+            'front_end/Checkbox.js',
+            'front_end/Color.js',
+            'front_end/CompilerScriptMapping.js',
+            'front_end/ConsoleMessage.js',
+            'front_end/ConsoleModel.js',
+            'front_end/ConsolePanel.js',
+            'front_end/ConsoleView.js',
+            'front_end/ContentProvider.js',
+            'front_end/ContentProviderBasedProjectDelegate.js',
+            'front_end/ContentProviders.js',
+            'front_end/ContextMenu.js',
+            'front_end/CookieItemsView.js',
+            'front_end/CookieParser.js',
+            'front_end/CookiesTable.js',
+            'front_end/CSSMetadata.js',
+            'front_end/CSSStyleModel.js',
+            'front_end/Database.js',
+            'front_end/DataGrid.js',
+            'front_end/DebuggerModel.js',
+            'front_end/DebuggerScriptMapping.js',
+            'front_end/DefaultTextEditor.js',
+            'front_end/DevToolsExtensionAPI.js',
+            'front_end/Tests.js',
+            'front_end/Dialog.js',
+            'front_end/DOMAgent.js',
+            'front_end/DOMBreakpointsSidebarPane.js',
+            'front_end/DOMCountersGraph.js',
+            'front_end/DOMExtension.js',
+            'front_end/DOMPresentationUtils.js',
+            'front_end/DOMStorage.js',
+            'front_end/DOMSyntaxHighlighter.js',
+            'front_end/DefaultScriptMapping.js',
+            'front_end/DockController.js',
+            'front_end/Drawer.js',
+            'front_end/ElementsPanelDescriptor.js',
+            'front_end/ElementsTreeOutline.js',
+            'front_end/EmptyView.js',
+            'front_end/ExtensionAPI.js',
+            'front_end/ExtensionAuditCategory.js',
+            'front_end/ExtensionPanel.js',
+            'front_end/ExtensionRegistryStub.js',
+            'front_end/ExtensionServer.js',
+            'front_end/ExtensionView.js',
+            'front_end/FileManager.js',
+            'front_end/FileMapping.js',
+            'front_end/FileSystemMapping.js',
+            'front_end/FileSystemModel.js',
+            'front_end/FileSystemProjectDelegate.js',
+            'front_end/FileUtils.js',
+            'front_end/FontView.js',
+            'front_end/GoToLineDialog.js',
+            'front_end/HAREntry.js',
+            'front_end/HandlerRegistry.js',
+            'front_end/HeapSnapshotWorker.js',
+            'front_end/HelpScreen.js',
+            'front_end/ImageView.js',
+            'front_end/IndexedDBModel.js',
+            'front_end/InspectorBackend.js',
+            'front_end/InspectorFrontendAPI.js',
+            'front_end/InspectorFrontendHostStub.js',
+            'front_end/InspectorView.js',
+            'front_end/inspector.js',
+            'front_end/IsolatedFileSystem.js',
+            'front_end/IsolatedFileSystemManager.js',
+            'front_end/JavaScriptFormatter.js',
+            'front_end/jsdifflib.js',
+            'front_end/KeyboardShortcut.js',
+            'front_end/Linkifier.js',
+            'front_end/LiveEditSupport.js',
+            'front_end/NativeBreakpointsSidebarPane.js',
+            'front_end/NetworkManager.js',
+            'front_end/NetworkLog.js',
+            'front_end/NetworkPanelDescriptor.js',
+            'front_end/NetworkRequest.js',
+            'front_end/NetworkUISourceCodeProvider.js',
+            'front_end/InspectElementModeController.js',
+            'front_end/Object.js',
+            'front_end/ObjectPopoverHelper.js',
+            'front_end/ObjectPropertiesSection.js',
+            'front_end/OverridesView.js',
+            'front_end/OverviewGrid.js',
+            'front_end/Panel.js',
+            'front_end/ParsedURL.js',
+            'front_end/Placard.js',
+            'front_end/Popover.js',
+            'front_end/PresentationConsoleMessageHelper.js',
+            'front_end/ProfilesPanelDescriptor.js',
+            'front_end/Progress.js',
+            'front_end/ProgressIndicator.js',
+            'front_end/PropertiesSection.js',
+            'front_end/RemoteObject.js',
+            'front_end/Resource.js',
+            'front_end/ResourceScriptMapping.js',
+            'front_end/ResourceTreeModel.js',
+            'front_end/ResourceType.js',
+            'front_end/ResourceUtils.js',
+            'front_end/ResourceView.js',
+            'front_end/RuntimeModel.js',
+            'front_end/SASSSourceMapping.js',
+            'front_end/Script.js',
+            'front_end/ScriptFormatter.js',
+            'front_end/ScriptFormatterWorker.js',
+            'front_end/ScriptSnippetModel.js',
+            'front_end/ScriptsPanelDescriptor.js',
+            'front_end/SearchController.js',
+            'front_end/SettingsScreen.js',
+            'front_end/Section.js',
+            'front_end/Settings.js',
+            'front_end/ShortcutsScreen.js',
+            'front_end/ShowMoreDataGridNode.js',
+            'front_end/SidebarOverlay.js',
+            'front_end/SidebarPane.js',
+            'front_end/SidebarView.js',
+            'front_end/SidebarTreeElement.js',
+            'front_end/SimpleWorkspaceProvider.js',
+            'front_end/SnippetStorage.js',
+            'front_end/SoftContextMenu.js',
+            'front_end/SourceCSSTokenizer.js',
+            'front_end/SourceFrame.js',
+            'front_end/SourceHTMLTokenizer.js',
+            'front_end/SourceJavaScriptTokenizer.js',
+            'front_end/SourceMap.js',
+            'front_end/SourceMapping.js',
+            'front_end/SourceTokenizer.js',
+            'front_end/Spectrum.js',
+            'front_end/SplitView.js',
+            'front_end/StatusBarButton.js',
+            'front_end/StylesSourceMapping.js',
+            'front_end/SuggestBox.js',
+            'front_end/TabbedPane.js',
+            'front_end/TestController.js',
+            'front_end/TextEditor.js',
+            'front_end/TextEditorHighlighter.js',
+            'front_end/TextEditorModel.js',
+            'front_end/TextPrompt.js',
+            'front_end/TextUtils.js',
+            'front_end/TimelineGrid.js',
+            'front_end/TimelineManager.js',
+            'front_end/TimelinePanelDescriptor.js',
+            'front_end/Toolbar.js',
+            'front_end/treeoutline.js',
+            'front_end/UISourceCode.js',
+            'front_end/UIString.js',
+            'front_end/UIUtils.js',
+            'front_end/UserAgentSupport.js',
+            'front_end/UserMetrics.js',
+            'front_end/utilities.js',
+            'front_end/View.js',
+            'front_end/ViewportControl.js',
+            'front_end/WorkerManager.js',
+            'front_end/Workspace.js',
+            'front_end/dialog.css',
+            'front_end/inspector.css',
+            'front_end/inspectorSyntaxHighlight.css',
+            'front_end/popover.css',
+            '<@(devtools_modules_js_files)',
+            '<@(devtools_standalone_files)',
+        ],
+        'devtools_standalone_files': [
+            'front_end/auditsPanel.css',
+            'front_end/breadcrumbList.css',
+            'front_end/breakpointsList.css',
+            'front_end/buildSystemOnly.js',
+            'front_end/cm/cmdevtools.css',
+            'front_end/cm/codemirror.css',
+            'front_end/cssNamedFlows.css',
+            'front_end/dataGrid.css',
+            'front_end/elementsPanel.css',
+            'front_end/filteredItemSelectionDialog.css',
+            'front_end/flameChart.css',
+            'front_end/heapProfiler.css',
+            'front_end/helpScreen.css',
+            'front_end/indexedDBViews.css',
+            'front_end/inspectorCommon.css',
+            'front_end/nativeMemoryProfiler.css',
+            'front_end/navigatorView.css',
+            'front_end/networkLogView.css',
+            'front_end/networkPanel.css',
+            'front_end/panelEnablerView.css',
+            'front_end/profilesPanel.css',
+            'front_end/resourceView.css',
+            'front_end/resourcesPanel.css',
+            'front_end/revisionHistory.css',
+            'front_end/scriptsPanel.css',
+            'front_end/sidebarPane.css',
+            'front_end/spectrum.css',
+            'front_end/splitView.css',
+            'front_end/tabbedPane.css',
+            'front_end/textEditor.css',
+            'front_end/textPrompt.css',
+            'front_end/timelinePanel.css',
+            'front_end/canvasProfiler.css',
+        ],
+        'devtools_elements_js_files': [
+            'front_end/CSSNamedFlowCollectionsView.js',
+            'front_end/CSSNamedFlowView.js',
+            'front_end/ElementsPanel.js',
+            'front_end/EventListenersSidebarPane.js',
+            'front_end/MetricsSidebarPane.js',
+            'front_end/PropertiesSidebarPane.js',
+            'front_end/StylesSidebarPane.js',
+        ],
+        'devtools_resources_js_files': [
+            'front_end/ApplicationCacheItemsView.js',
+            'front_end/DOMStorageItemsView.js',
+            'front_end/DatabaseQueryView.js',
+            'front_end/DatabaseTableView.js',
+            'front_end/DirectoryContentView.js',
+            'front_end/FileContentView.js',
+            'front_end/FileSystemView.js',
+            'front_end/IndexedDBViews.js',
+            'front_end/ResourcesPanel.js',
+        ],
+        'devtools_network_js_files': [
+            'front_end/NetworkItemView.js',
+            'front_end/RequestCookiesView.js',
+            'front_end/RequestHeadersView.js',
+            'front_end/RequestHTMLView.js',
+            'front_end/RequestJSONView.js',
+            'front_end/RequestPreviewView.js',
+            'front_end/RequestResponseView.js',
+            'front_end/RequestTimingView.js',
+            'front_end/RequestView.js',
+            'front_end/ResourceWebSocketFrameView.js',
+            'front_end/NetworkPanel.js',
+        ],
+        'devtools_scripts_js_files': [
+            'front_end/BreakpointsSidebarPane.js',
+            'front_end/CallStackSidebarPane.js',
+            'front_end/FilteredItemSelectionDialog.js',
+            'front_end/JavaScriptSourceFrame.js',
+            'front_end/NavigatorOverlayController.js',
+            'front_end/NavigatorView.js',
+            'front_end/RevisionHistoryView.js',
+            'front_end/ScopeChainSidebarPane.js',
+            'front_end/ScriptsNavigator.js',
+            'front_end/ScriptsPanel.js',
+            'front_end/ScriptsSearchScope.js',
+            'front_end/StyleSheetOutlineDialog.js',
+            'front_end/TabbedEditorContainer.js',
+            'front_end/UISourceCodeFrame.js',
+            'front_end/WatchExpressionsSidebarPane.js',
+            'front_end/WorkersSidebarPane.js',
+        ],
+        'devtools_timeline_js_files': [
+            'front_end/MemoryStatistics.js',
+            'front_end/NativeMemoryGraph.js',
+            'front_end/TimelineFrameController.js',
+            'front_end/TimelineModel.js',
+            'front_end/TimelinePresentationModel.js',
+            'front_end/TimelineOverviewPane.js',
+            'front_end/TimelinePanel.js',
+        ],
+
+        'devtools_profiles_js_files': [
+            'front_end/BottomUpProfileDataGridTree.js',
+            'front_end/CPUProfileView.js',
+            'front_end/CSSSelectorProfileView.js',
+            'front_end/FlameChart.js',
+            'front_end/HeapSnapshot.js',
+            'front_end/HeapSnapshotDataGrids.js',
+            'front_end/HeapSnapshotGridNodes.js',
+            'front_end/HeapSnapshotLoader.js',
+            'front_end/HeapSnapshotProxy.js',
+            'front_end/HeapSnapshotView.js',
+            'front_end/HeapSnapshotWorkerDispatcher.js',
+            'front_end/JSHeapSnapshot.js',
+            'front_end/NativeHeapSnapshot.js',
+            'front_end/NativeMemorySnapshotView.js',
+            'front_end/ProfileDataGridTree.js',
+            'front_end/ProfilesPanel.js',
+            'front_end/ProfileLauncherView.js',
+            'front_end/TopDownProfileDataGridTree.js',
+            'front_end/CanvasProfileView.js',
+        ],
+
+        'devtools_audits_js_files': [
+            'front_end/AuditCategories.js',
+            'front_end/AuditController.js',
+            'front_end/AuditFormatters.js',
+            'front_end/AuditLauncherView.js',
+            'front_end/AuditResultView.js',
+            'front_end/AuditRules.js',
+            'front_end/AuditsPanel.js',
+        ],
+
+        'devtools_codemirror_js_files': [
+            'front_end/CodeMirrorTextEditor.js',
+            'front_end/cm/codemirror.js',
+            'front_end/cm/css.js',
+            'front_end/cm/htmlmixed.js',
+            'front_end/cm/closebrackets.js',
+            'front_end/cm/matchbrackets.js',
+            'front_end/cm/javascript.js',
+            'front_end/cm/xml.js',
+            'front_end/cm/markselection.js',
+        ],
+
+        'devtools_modules_js_files': [
+            '<@(devtools_elements_js_files)',
+            '<@(devtools_resources_js_files)',
+            '<@(devtools_network_js_files)',
+            '<@(devtools_scripts_js_files)',
+            '<@(devtools_timeline_js_files)',
+            '<@(devtools_profiles_js_files)',
+            '<@(devtools_audits_js_files)',
+            '<@(devtools_codemirror_js_files)',
+        ],
+        'devtools_uglifyjs_files': [
+            'front_end/UglifyJS/parse-js.js',
+        ],
+        'devtools_image_files': [
+            'front_end/Images/addIcon.png',
+            'front_end/Images/applicationCache.png',
+            'front_end/Images/back.png',
+            'front_end/Images/breakpointBorder.png',
+            'front_end/Images/breakpoint2.png',
+            'front_end/Images/breakpoint2_2x.png',
+            'front_end/Images/breakpointConditional2.png',
+            'front_end/Images/breakpointConditional2_2x.png',
+            'front_end/Images/breakpointConditionalBorder.png',
+            'front_end/Images/breakpointConditionalCounterBorder.png',
+            'front_end/Images/breakpointCounterBorder.png',
+            'front_end/Images/checker.png',
+            'front_end/Images/cookie.png',
+            'front_end/Images/namedFlowOverflow.png',
+            'front_end/Images/database.png',
+            'front_end/Images/databaseTable.png',
+            'front_end/Images/deleteIcon.png',
+            'front_end/Images/domain.png',
+            'front_end/Images/forward.png',
+            'front_end/Images/fileSystem.png',
+            'front_end/Images/frame.png',
+            'front_end/Images/glossyHeader.png',
+            'front_end/Images/glossyHeaderPressed.png',
+            'front_end/Images/glossyHeaderSelected.png',
+            'front_end/Images/glossyHeaderSelectedPressed.png',
+            'front_end/Images/graphLabelCalloutLeft.png',
+            'front_end/Images/graphLabelCalloutRight.png',
+            'front_end/Images/indexedDB.png',
+            'front_end/Images/indexedDBObjectStore.png',
+            'front_end/Images/indexedDBIndex.png',
+            'front_end/Images/localStorage.png',
+            'front_end/Images/paneAddButtons.png',
+            'front_end/Images/paneElementStateButtons.png',
+            'front_end/Images/paneFilterButtons.png',
+            'front_end/Images/paneRefreshButtons.png',
+            'front_end/Images/paneSettingsButtons.png',
+            'front_end/Images/popoverArrows.png',
+            'front_end/Images/popoverBackground.png',
+            'front_end/Images/profileGroupIcon.png',
+            'front_end/Images/profileIcon.png',
+            'front_end/Images/profileSmallIcon.png',
+            'front_end/Images/programCounterBorder.png',
+            'front_end/Images/radioDot.png',
+            'front_end/Images/regionEmpty.png',
+            'front_end/Images/regionFit.png',
+            'front_end/Images/regionOverset.png',
+            'front_end/Images/resourceCSSIcon.png',
+            'front_end/Images/resourceDocumentIcon.png',
+            'front_end/Images/resourceDocumentIconSmall.png',
+            'front_end/Images/resourceJSIcon.png',
+            'front_end/Images/resourcePlainIcon.png',
+            'front_end/Images/resourcePlainIconSmall.png',
+            'front_end/Images/resourcesTimeGraphIcon.png',
+            'front_end/Images/searchSmallBlue.png',
+            'front_end/Images/searchSmallBrightBlue.png',
+            'front_end/Images/searchSmallGray.png',
+            'front_end/Images/searchSmallWhite.png',
+            'front_end/Images/searchNext.png',
+            'front_end/Images/searchPrev.png',
+            'front_end/Images/segment.png',
+            'front_end/Images/segmentEnd.png',
+            'front_end/Images/segmentHover.png',
+            'front_end/Images/segmentHoverEnd.png',
+            'front_end/Images/segmentSelected.png',
+            'front_end/Images/segmentSelectedEnd.png',
+            'front_end/Images/sessionStorage.png',
+            'front_end/Images/spinner.gif',
+            'front_end/Images/spinnerActive.gif',
+            'front_end/Images/spinnerActiveSelected.gif',
+            'front_end/Images/spinnerInactive.gif',
+            'front_end/Images/spinnerInactiveSelected.gif',
+            'front_end/Images/statusbarButtonGlyphs.png',
+            'front_end/Images/statusbarButtonGlyphs2x.png',
+            'front_end/Images/statusbarResizerHorizontal.png',
+            'front_end/Images/statusbarResizerVertical.png',
+            'front_end/Images/thumbActiveHoriz.png',
+            'front_end/Images/thumbActiveVert.png',
+            'front_end/Images/thumbHoriz.png',
+            'front_end/Images/thumbVert.png',
+            'front_end/Images/thumbHoverHoriz.png',
+            'front_end/Images/thumbHoverVert.png',
+            'front_end/Images/timelineHollowPillBlue.png',
+            'front_end/Images/timelineHollowPillGray.png',
+            'front_end/Images/timelineHollowPillGreen.png',
+            'front_end/Images/timelineHollowPillOrange.png',
+            'front_end/Images/timelineHollowPillPurple.png',
+            'front_end/Images/timelineHollowPillRed.png',
+            'front_end/Images/timelineHollowPillYellow.png',
+            'front_end/Images/timelinePillBlue.png',
+            'front_end/Images/timelinePillGray.png',
+            'front_end/Images/timelinePillGreen.png',
+            'front_end/Images/timelinePillOrange.png',
+            'front_end/Images/timelinePillPurple.png',
+            'front_end/Images/timelinePillRed.png',
+            'front_end/Images/timelinePillYellow.png',
+            'front_end/Images/toolbarIcons.png',
+            'front_end/Images/toolbarIconsSmall.png',
+            'front_end/Images/toolbarItemSelected.png',
+            'front_end/Images/trackHoriz.png',
+            'front_end/Images/trackVert.png',
+        ],
+        'devtools_extension_api_files': [
+            'front_end/ExtensionAPI.js',
+            'front_end/DevToolsExtensionAPI.js'
+        ],
+    },
+    'targets': [
+        {
+            'target_name': 'devtools_frontend_resources',
+            'type': 'none',
+            'dependencies': [
+                'devtools_html',
+                'frontend_protocol_sources',
+            ],
+            'conditions': [
+                ['debug_devtools==0', {
+                    'dependencies': ['concatenated_devtools_js',
+                                     'concatenated_devtools_elements_js',
+                                     'concatenated_devtools_resources_js',
+                                     'concatenated_devtools_network_js',
+                                     'concatenated_devtools_scripts_js',
+                                     'concatenated_devtools_timeline_js',
+                                     'concatenated_devtools_profiles_js',
+                                     'concatenated_devtools_audits_js',
+                                     'concatenated_devtools_codemirror_js',
+                                     'concatenated_heap_snapshot_worker_js',
+                                     'concatenated_script_formatter_worker_js',
+                                     'concatenated_devtools_css'],
+                }],
+            ],
+            'copies': [
+                {
+                    'destination': '<(PRODUCT_DIR)/resources/inspector',
+                    'files': [
+                        '<@(devtools_files)',
+                        '<(SHARED_INTERMEDIATE_DIR)/webcore/InspectorBackendCommands.js',
+                    ],
+                    'conditions': [
+                        ['debug_devtools==0', {
+                            'files/': [['exclude', '\\.(js|css|html)$']],
+                        }],
+                    ],
+                },
+                {
+                    'destination': '<(PRODUCT_DIR)/resources/inspector/UglifyJS',
+                    'files': [
+                        '<@(devtools_uglifyjs_files)',
+                    ],
+                    'conditions': [
+                        ['debug_devtools==0', {
+                            'files/': [['exclude', '\\.(js|css|html)$']],
+                        }],
+                    ],
+                },
+                {
+                    'destination': '<(PRODUCT_DIR)/resources/inspector/Images',
+                    'files': [
+                        '<@(devtools_image_files)',
+                    ],
+                },
+            ],
+        },
+        {
+            'target_name': 'devtools_html',
+            'type': 'none',
+            'sources': ['<(PRODUCT_DIR)/resources/inspector/devtools.html'],
+            'actions': [{
+                'action_name': 'devtools_html',
+                'script_name': 'scripts/generate_devtools_html.py',
+                'input_page': 'front_end/inspector.html',
+                'inputs': [
+                    '<@(_script_name)',
+                    '<@(_input_page)',
+                ],
+                'outputs': ['<(PRODUCT_DIR)/resources/inspector/devtools.html'],
+                'action': ['python', '<@(_script_name)', '<@(_input_page)', '<@(_outputs)', '<@(debug_devtools)'],
+            }],
+        },
+        {
+            'target_name': 'devtools_extension_api',
+            'type': 'none',
+            'actions': [{
+                'action_name': 'devtools_html',
+                'script_name': 'scripts/generate_devtools_extension_api.py',
+                'inputs': [
+                    '<@(_script_name)',
+                    '<@(devtools_extension_api_files)',
+                ],
+                'outputs': ['<(PRODUCT_DIR)/resources/inspector/devtools_extension_api.js'],
+                'action': ['python', '<@(_script_name)', '<@(_outputs)', '<@(devtools_extension_api_files)'],
+            }],
+        },
+        {
+            'target_name': 'generate_devtools_grd',
+            'type': 'none',
+            'dependencies': [
+                'devtools_html',
+                'devtools_extension_api'
+            ],
+            'conditions': [
+                ['debug_devtools==0', {
+                    'dependencies': ['concatenated_devtools_js',
+                                     'concatenated_devtools_elements_js',
+                                     'concatenated_devtools_resources_js',
+                                     'concatenated_devtools_network_js',
+                                     'concatenated_devtools_scripts_js',
+                                     'concatenated_devtools_timeline_js',
+                                     'concatenated_devtools_profiles_js',
+                                     'concatenated_devtools_audits_js',
+                                     'concatenated_devtools_codemirror_js',
+                                     'concatenated_heap_snapshot_worker_js',
+                                     'concatenated_script_formatter_worker_js',
+                                     'concatenated_devtools_css'],
+                    'actions': [{
+                        'action_name': 'generate_devtools_grd',
+                        'script_name': 'scripts/generate_devtools_grd.py',
+                        'input_pages': [
+                            '<(PRODUCT_DIR)/resources/inspector/devtools.html',
+                            '<(PRODUCT_DIR)/resources/inspector/inspector.js',
+                            '<(PRODUCT_DIR)/resources/inspector/ElementsPanel.js',
+                            '<(PRODUCT_DIR)/resources/inspector/ResourcesPanel.js',
+                            '<(PRODUCT_DIR)/resources/inspector/NetworkPanel.js',
+                            '<(PRODUCT_DIR)/resources/inspector/ScriptsPanel.js',
+                            '<(PRODUCT_DIR)/resources/inspector/TimelinePanel.js',
+                            '<(PRODUCT_DIR)/resources/inspector/ProfilesPanel.js',
+                            '<(PRODUCT_DIR)/resources/inspector/AuditsPanel.js',
+                            '<(PRODUCT_DIR)/resources/inspector/CodeMirrorTextEditor.js',
+                            '<(PRODUCT_DIR)/resources/inspector/HeapSnapshotWorker.js',
+                            '<(PRODUCT_DIR)/resources/inspector/ScriptFormatterWorker.js',
+                            '<(PRODUCT_DIR)/resources/inspector/inspector.css',
+                            '<(PRODUCT_DIR)/resources/inspector/devtools_extension_api.js',
+                            '<@(devtools_standalone_files)',
+                        ],
+                        'images': [
+                            '<@(devtools_image_files)',
+                        ],
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(_input_pages)',
+                            '<@(_images)',
+                        ],
+                        'search_path': [
+                            'front_end/Images',
+                        ],
+                        'outputs': ['<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_pages)', '--images', '<@(_search_path)', '--output', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    # If we're not concatenating devtools files, we want to
+                    # run after the original files have been copied to
+                    # <(PRODUCT_DIR)/resources/inspector.
+                    'dependencies': ['devtools_frontend_resources'],
+                    'actions': [{
+                        'action_name': 'generate_devtools_grd',
+                        'script_name': 'scripts/generate_devtools_grd.py',
+                        'input_pages': [
+                            '<@(devtools_files)',
+                            '<(SHARED_INTERMEDIATE_DIR)/webcore/InspectorBackendCommands.js',
+                            '<(PRODUCT_DIR)/resources/inspector/devtools.html',
+                        ],
+                        'images': [
+                            '<@(devtools_image_files)',
+                        ],
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(_input_pages)',
+                            '<@(_images)',
+                        ],
+                        'search_path': [
+                            'front_end/Images',
+                        ],
+                        # Note that other files are put under /devtools directory, together with declared devtools_resources.grd
+                        'outputs': ['<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_pages)', '--images', '<@(_search_path)', '--output', '<@(_outputs)'],
+                    }],
+                }],
+            ],
+        },
+	    {
+	      'target_name': 'frontend_protocol_sources',
+	      'type': 'none',
+	      'actions': [
+	        {
+	          'action_name': 'generateInspectorProtocolFrontendSources',
+	          'inputs': [
+	            # The python script in action below.
+	            'scripts/CodeGeneratorFrontend.py',
+	            # Input file for the script.
+	            'protocol.json',
+	          ],
+	          'outputs': [
+	            '<(SHARED_INTERMEDIATE_DIR)/webcore/InspectorBackendCommands.js',
+	          ],
+	          'action': [
+	            'python',
+	            'scripts/CodeGeneratorFrontend.py',
+	            'protocol.json',
+	            '--output_js_dir', '<(SHARED_INTERMEDIATE_DIR)/webcore',
+	          ],
+	          'message': 'Generating Inspector protocol frontend sources from protocol.json',
+	          'msvs_cygwin_shell': 1,
+	        },
+	      ]
+	    },
+    ], # targets
+    'conditions': [
+        ['debug_devtools==0', {
+            'targets': [
+                {
+                    'target_name': 'concatenated_devtools_js',
+                    'type': 'none',
+                    'dependencies': [
+                        'devtools_html',
+                        'frontend_protocol_sources'
+                    ],
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_js',
+                        'script_name': 'scripts/concatenate_js_files.py',
+                        'input_page': 'front_end/inspector.html',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(_input_page)',
+                            '<@(devtools_files)',
+                            '<(SHARED_INTERMEDIATE_DIR)/webcore/InspectorBackendCommands.js'
+                        ],
+                        'search_path': [
+                            'front_end',
+                            '<(SHARED_INTERMEDIATE_DIR)/webcore',
+                        ],
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/inspector.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_page)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_devtools_elements_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_elements_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/ElementsPanel.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(devtools_elements_js_files)',
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/ElementsPanel.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_devtools_resources_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_resources_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/ResourcesPanel.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(devtools_resources_js_files)',
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/ResourcesPanel.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_devtools_network_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_network_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/NetworkPanel.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(devtools_network_js_files)',
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/NetworkPanel.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_devtools_scripts_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_scripts_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/ScriptsPanel.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(devtools_scripts_js_files)',
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/ScriptsPanel.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_devtools_timeline_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_timeline_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/TimelinePanel.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(devtools_timeline_js_files)',
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/TimelinePanel.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_devtools_profiles_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_profiles_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/ProfilesPanel.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(devtools_profiles_js_files)',
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/ProfilesPanel.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_devtools_audits_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_audits_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/AuditsPanel.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(devtools_audits_js_files)',
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/AuditsPanel.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_devtools_codemirror_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_codemirror_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/CodeMirrorTextEditor.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(devtools_codemirror_js_files)',
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/CodeMirrorTextEditor.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)', 'true'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_heap_snapshot_worker_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_heap_snapshot_worker_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/HeapSnapshotWorker.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(_input_file)',
+                            'front_end/HeapSnapshot.js',
+                            'front_end/HeapSnapshotLoader.js',
+                            'front_end/HeapSnapshotWorkerDispatcher.js',
+                            'front_end/JSHeapSnapshot.js',
+                            'front_end/utilities.js',
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/HeapSnapshotWorker.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_script_formatter_worker_js',
+                    'type': 'none',
+                    'actions': [{
+                        'action_name': 'concatenate_script_formatter_worker_js',
+                        'script_name': 'scripts/inline_js_imports.py',
+                        'input_file': 'front_end/ScriptFormatterWorker.js',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(_input_file)',
+                            '<@(devtools_uglifyjs_files)'
+                        ],
+                        'search_path': 'front_end',
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/ScriptFormatterWorker.js'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_file)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                },
+                {
+                    'target_name': 'concatenated_devtools_css',
+                    'type': 'none',
+                    'dependencies': [
+                        'devtools_html'
+                    ],
+                    'actions': [{
+                        'action_name': 'concatenate_devtools_css',
+                        'script_name': 'scripts/concatenate_css_files.py',
+                        'input_page': 'front_end/inspector.html',
+                        'inputs': [
+                            '<@(_script_name)',
+                            '<@(_input_page)',
+                            '<@(devtools_files)',
+                        ],
+                        'search_path': [ 'front_end' ],
+                        'outputs': ['<(PRODUCT_DIR)/resources/inspector/inspector.css'],
+                        'action': ['python', '<@(_script_name)', '<@(_input_page)', '<@(_search_path)', '<@(_outputs)'],
+                    }],
+                    'copies': [{
+                        'destination': '<(PRODUCT_DIR)/resources/inspector',
+                        'files': [
+                            '<@(devtools_standalone_files)',
+                        ],
+                    }],
+                },
+            ],
+        }],
+    ], # conditions
+}
diff --git a/Source/devtools/devtools_extension_api.target.darwin-arm.mk b/Source/devtools/devtools_extension_api.target.darwin-arm.mk
new file mode 100644
index 0000000..fc7a1d3
--- /dev/null
+++ b/Source/devtools/devtools_extension_api.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+LOCAL_MODULE_STEM := devtools_extension_api
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "devtools_html":
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_extension_api.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_devtools_extension_api_target_devtools_html ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/generate_devtools_extension_api.py "$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js" front_end/ExtensionAPI.js front_end/DevToolsExtensionAPI.js
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+
+# Alias gyp target name.
+.PHONY: devtools_extension_api
+devtools_extension_api: third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/devtools_extension_api.target.darwin-x86.mk b/Source/devtools/devtools_extension_api.target.darwin-x86.mk
new file mode 100644
index 0000000..fc7a1d3
--- /dev/null
+++ b/Source/devtools/devtools_extension_api.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+LOCAL_MODULE_STEM := devtools_extension_api
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "devtools_html":
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_extension_api.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_devtools_extension_api_target_devtools_html ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/generate_devtools_extension_api.py "$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js" front_end/ExtensionAPI.js front_end/DevToolsExtensionAPI.js
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+
+# Alias gyp target name.
+.PHONY: devtools_extension_api
+devtools_extension_api: third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/devtools_extension_api.target.linux-arm.mk b/Source/devtools/devtools_extension_api.target.linux-arm.mk
new file mode 100644
index 0000000..fc7a1d3
--- /dev/null
+++ b/Source/devtools/devtools_extension_api.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+LOCAL_MODULE_STEM := devtools_extension_api
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "devtools_html":
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_extension_api.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_devtools_extension_api_target_devtools_html ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/generate_devtools_extension_api.py "$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js" front_end/ExtensionAPI.js front_end/DevToolsExtensionAPI.js
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+
+# Alias gyp target name.
+.PHONY: devtools_extension_api
+devtools_extension_api: third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/devtools_extension_api.target.linux-x86.mk b/Source/devtools/devtools_extension_api.target.linux-x86.mk
new file mode 100644
index 0000000..fc7a1d3
--- /dev/null
+++ b/Source/devtools/devtools_extension_api.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+LOCAL_MODULE_STEM := devtools_extension_api
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "devtools_html":
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_extension_api.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/ExtensionAPI.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/DevToolsExtensionAPI.js $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_devtools_extension_api_target_devtools_html ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/generate_devtools_extension_api.py "$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js" front_end/ExtensionAPI.js front_end/DevToolsExtensionAPI.js
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+
+# Alias gyp target name.
+.PHONY: devtools_extension_api
+devtools_extension_api: third_party_WebKit_Source_devtools_devtools_extension_api_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/devtools_html.target.darwin-arm.mk b/Source/devtools/devtools_html.target.darwin-arm.mk
new file mode 100644
index 0000000..c3af118
--- /dev/null
+++ b/Source/devtools/devtools_html.target.darwin-arm.mk
@@ -0,0 +1,138 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_devtools_html_gyp
+LOCAL_MODULE_STEM := devtools_html
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "devtools_html":
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_html.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_devtools_html_target_devtools_html ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/generate_devtools_html.py front_end/inspector.html "$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html" 0
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+LOCAL_GENERATED_SOURCES :=
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS :=
+
+LOCAL_SRC_FILES :=
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+	-fstack-protector \
+	--param=ssp-buffer-size=4 \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-Wno-format \
+	-fno-tree-sra \
+	-fuse-ld=gold \
+	-Wno-psabi \
+	-ffunction-sections \
+	-funwind-tables \
+	-g \
+	-fstack-protector \
+	-fno-short-enums \
+	-finline-limit=64 \
+	-Wa,--noexecstack \
+	-U_FORTIFY_SOURCE \
+	-Wno-extra \
+	-Wno-ignored-qualifiers \
+	-Wno-type-limits \
+	-Wno-address \
+	-Wno-format-security \
+	-Wno-return-type \
+	-Wno-sequence-point \
+	-Os \
+	-g \
+	-fomit-frame-pointer \
+	-fdata-sections \
+	-ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DNO_TCMALLOC' \
+	'-DDISABLE_NACL' \
+	'-DCHROMIUM_BUILD' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_PROPRIETARY_CODECS' \
+	'-DENABLE_GPU=1' \
+	'-DUSE_OPENSSL=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-DANDROID' \
+	'-D__GNU_SOURCE=1' \
+	'-DUSE_STLPORT=1' \
+	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+	'-DCHROME_BUILD_ID=""' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
+	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
+	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wno-deprecated \
+	-Wno-abi \
+	-Wno-error=c++0x-compat \
+	-Wno-non-virtual-dtor \
+	-Wno-sign-promo \
+	-Wno-non-virtual-dtor
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_devtools_html_gyp
+
+# Alias gyp target name.
+.PHONY: devtools_html
+devtools_html: third_party_WebKit_Source_devtools_devtools_html_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/devtools_html.target.darwin-x86.mk b/Source/devtools/devtools_html.target.darwin-x86.mk
new file mode 100644
index 0000000..a9e4ee0
--- /dev/null
+++ b/Source/devtools/devtools_html.target.darwin-x86.mk
@@ -0,0 +1,139 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_devtools_html_gyp
+LOCAL_MODULE_STEM := devtools_html
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "devtools_html":
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_html.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_devtools_html_target_devtools_html ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/generate_devtools_html.py front_end/inspector.html "$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html" 0
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+LOCAL_GENERATED_SOURCES :=
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS :=
+
+LOCAL_SRC_FILES :=
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+	--param=ssp-buffer-size=4 \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-Wno-format \
+	-m32 \
+	-mmmx \
+	-march=pentium4 \
+	-msse2 \
+	-mfpmath=sse \
+	-fuse-ld=gold \
+	-ffunction-sections \
+	-funwind-tables \
+	-g \
+	-fno-short-enums \
+	-finline-limit=64 \
+	-Wa,--noexecstack \
+	-U_FORTIFY_SOURCE \
+	-Wno-extra \
+	-Wno-ignored-qualifiers \
+	-Wno-type-limits \
+	-Wno-address \
+	-Wno-format-security \
+	-Wno-return-type \
+	-Wno-sequence-point \
+	-fno-stack-protector \
+	-Os \
+	-g \
+	-fomit-frame-pointer \
+	-fdata-sections \
+	-ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DNO_TCMALLOC' \
+	'-DDISABLE_NACL' \
+	'-DCHROMIUM_BUILD' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_PROPRIETARY_CODECS' \
+	'-DENABLE_GPU=1' \
+	'-DUSE_OPENSSL=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-DANDROID' \
+	'-D__GNU_SOURCE=1' \
+	'-DUSE_STLPORT=1' \
+	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+	'-DCHROME_BUILD_ID=""' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
+	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
+	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wno-deprecated \
+	-Wno-error=c++0x-compat \
+	-Wno-non-virtual-dtor \
+	-Wno-sign-promo \
+	-Wno-non-virtual-dtor
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_devtools_html_gyp
+
+# Alias gyp target name.
+.PHONY: devtools_html
+devtools_html: third_party_WebKit_Source_devtools_devtools_html_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/devtools_html.target.linux-arm.mk b/Source/devtools/devtools_html.target.linux-arm.mk
new file mode 100644
index 0000000..c3af118
--- /dev/null
+++ b/Source/devtools/devtools_html.target.linux-arm.mk
@@ -0,0 +1,138 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_devtools_html_gyp
+LOCAL_MODULE_STEM := devtools_html
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "devtools_html":
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_html.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_devtools_html_target_devtools_html ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/generate_devtools_html.py front_end/inspector.html "$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html" 0
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+LOCAL_GENERATED_SOURCES :=
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS :=
+
+LOCAL_SRC_FILES :=
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+	-fstack-protector \
+	--param=ssp-buffer-size=4 \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-Wno-format \
+	-fno-tree-sra \
+	-fuse-ld=gold \
+	-Wno-psabi \
+	-ffunction-sections \
+	-funwind-tables \
+	-g \
+	-fstack-protector \
+	-fno-short-enums \
+	-finline-limit=64 \
+	-Wa,--noexecstack \
+	-U_FORTIFY_SOURCE \
+	-Wno-extra \
+	-Wno-ignored-qualifiers \
+	-Wno-type-limits \
+	-Wno-address \
+	-Wno-format-security \
+	-Wno-return-type \
+	-Wno-sequence-point \
+	-Os \
+	-g \
+	-fomit-frame-pointer \
+	-fdata-sections \
+	-ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DNO_TCMALLOC' \
+	'-DDISABLE_NACL' \
+	'-DCHROMIUM_BUILD' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_PROPRIETARY_CODECS' \
+	'-DENABLE_GPU=1' \
+	'-DUSE_OPENSSL=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-DANDROID' \
+	'-D__GNU_SOURCE=1' \
+	'-DUSE_STLPORT=1' \
+	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+	'-DCHROME_BUILD_ID=""' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
+	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
+	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wno-deprecated \
+	-Wno-abi \
+	-Wno-error=c++0x-compat \
+	-Wno-non-virtual-dtor \
+	-Wno-sign-promo \
+	-Wno-non-virtual-dtor
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_devtools_html_gyp
+
+# Alias gyp target name.
+.PHONY: devtools_html
+devtools_html: third_party_WebKit_Source_devtools_devtools_html_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/devtools_html.target.linux-x86.mk b/Source/devtools/devtools_html.target.linux-x86.mk
new file mode 100644
index 0000000..a9e4ee0
--- /dev/null
+++ b/Source/devtools/devtools_html.target.linux-x86.mk
@@ -0,0 +1,139 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_devtools_html_gyp
+LOCAL_MODULE_STEM := devtools_html
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "devtools_html":
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_html.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspector.html $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_devtools_html_target_devtools_html ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/resources/inspector; python scripts/generate_devtools_html.py front_end/inspector.html "$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html" 0
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+LOCAL_GENERATED_SOURCES :=
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS :=
+
+LOCAL_SRC_FILES :=
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+	--param=ssp-buffer-size=4 \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-Wno-format \
+	-m32 \
+	-mmmx \
+	-march=pentium4 \
+	-msse2 \
+	-mfpmath=sse \
+	-fuse-ld=gold \
+	-ffunction-sections \
+	-funwind-tables \
+	-g \
+	-fno-short-enums \
+	-finline-limit=64 \
+	-Wa,--noexecstack \
+	-U_FORTIFY_SOURCE \
+	-Wno-extra \
+	-Wno-ignored-qualifiers \
+	-Wno-type-limits \
+	-Wno-address \
+	-Wno-format-security \
+	-Wno-return-type \
+	-Wno-sequence-point \
+	-fno-stack-protector \
+	-Os \
+	-g \
+	-fomit-frame-pointer \
+	-fdata-sections \
+	-ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DNO_TCMALLOC' \
+	'-DDISABLE_NACL' \
+	'-DCHROMIUM_BUILD' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_PROPRIETARY_CODECS' \
+	'-DENABLE_GPU=1' \
+	'-DUSE_OPENSSL=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-DANDROID' \
+	'-D__GNU_SOURCE=1' \
+	'-DUSE_STLPORT=1' \
+	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+	'-DCHROME_BUILD_ID=""' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+	$(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
+	$(GYP_ABS_ANDROID_TOP_DIR)/bionic \
+	$(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wno-deprecated \
+	-Wno-error=c++0x-compat \
+	-Wno-non-virtual-dtor \
+	-Wno-sign-promo \
+	-Wno-non-virtual-dtor
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_devtools_html_gyp
+
+# Alias gyp target name.
+.PHONY: devtools_html
+devtools_html: third_party_WebKit_Source_devtools_devtools_html_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/front_end/AdvancedSearchController.js b/Source/devtools/front_end/AdvancedSearchController.js
new file mode 100644
index 0000000..22f500e
--- /dev/null
+++ b/Source/devtools/front_end/AdvancedSearchController.js
@@ -0,0 +1,699 @@
+/*
+ * 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.AdvancedSearchController = function()
+{
+    this._shortcut = WebInspector.AdvancedSearchController.createShortcut();
+    this._searchId = 0;
+    
+    WebInspector.settings.advancedSearchConfig = WebInspector.settings.createSetting("advancedSearchConfig", new WebInspector.SearchConfig("", true, false));
+    
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, this._frameNavigated, this);
+}
+
+/**
+ * @return {!WebInspector.KeyboardShortcut.Descriptor}
+ */
+WebInspector.AdvancedSearchController.createShortcut = function()
+{
+    if (WebInspector.isMac())
+        return WebInspector.KeyboardShortcut.makeDescriptor("f", WebInspector.KeyboardShortcut.Modifiers.Meta | WebInspector.KeyboardShortcut.Modifiers.Alt);
+    else
+        return WebInspector.KeyboardShortcut.makeDescriptor("f", WebInspector.KeyboardShortcut.Modifiers.Ctrl | WebInspector.KeyboardShortcut.Modifiers.Shift);
+}
+
+WebInspector.AdvancedSearchController.prototype = {
+    /**
+     * @param {KeyboardEvent} event
+     * @return {boolean}
+     */
+    handleShortcut: function(event)
+    {
+        if (WebInspector.KeyboardShortcut.makeKeyFromEvent(event) === this._shortcut.key) {
+            if (!this._searchView || !this._searchView.isShowing() || this._searchView._search !== document.activeElement) {
+                WebInspector.showPanel("scripts");
+                this.show();
+            } else
+                this.close();
+            event.consume(true);
+            return true;
+        }
+        return false;
+    },
+
+    _frameNavigated: function()
+    {
+        this.resetSearch();
+    },
+
+    /**
+     * @param {WebInspector.SearchScope} searchScope
+     */
+    registerSearchScope: function(searchScope)
+    {
+        // FIXME: implement multiple search scopes.
+        this._searchScope = searchScope;
+    },
+
+    show: function()
+    {
+        if (!this._searchView)
+            this._searchView = new WebInspector.SearchView(this);
+        
+        this._searchView.syncToSelection();
+
+        if (this._searchView.isShowing())
+            this._searchView.focus();
+        else
+            WebInspector.showViewInDrawer(this._searchView._searchPanelElement, this._searchView, this.stopSearch.bind(this));
+    },
+
+    close: function()
+    {
+        this.stopSearch();
+        WebInspector.closeViewInDrawer();
+    },
+
+    /**
+     * @param {number} searchId
+     * @param {WebInspector.FileBasedSearchResultsPane.SearchResult} searchResult
+     */
+    _onSearchResult: function(searchId, searchResult)
+    {
+        if (searchId !== this._searchId)
+            return;
+
+        this._searchView.addSearchResult(searchResult);
+        if (!searchResult.searchMatches.length)
+            return;
+        
+        if (!this._searchResultsPane) 
+            this._searchResultsPane = this._currentSearchScope.createSearchResultsPane(this._searchConfig);        
+        this._searchView.resultsPane = this._searchResultsPane; 
+        this._searchResultsPane.addSearchResult(searchResult);
+    },
+    
+    /**
+     * @param {number} searchId
+     * @param {boolean} finished
+     */
+    _onSearchFinished: function(searchId, finished)
+    {
+        if (searchId !== this._searchId)
+            return;
+
+        if (!this._searchResultsPane)
+            this._searchView.nothingFound();
+        
+        this._searchView.searchFinished(finished);
+    },
+    
+    /**
+     * @param {WebInspector.SearchConfig} searchConfig
+     */
+    startSearch: function(searchConfig)
+    {
+        this.resetSearch();
+        ++this._searchId;
+
+        this._searchConfig = searchConfig;
+        // FIXME: this._currentSearchScope should be initialized based on searchConfig
+        this._currentSearchScope = this._searchScope;
+
+        var totalSearchResultsCount = this._currentSearchScope.performSearch(searchConfig, this._onSearchResult.bind(this, this._searchId), this._onSearchFinished.bind(this, this._searchId));
+        this._searchView.searchStarted(totalSearchResultsCount);
+    },
+    
+    resetSearch: function()
+    {
+        this.stopSearch();
+
+        if (this._searchResultsPane) {
+            this._searchView.resetResults();
+            delete this._searchResultsPane;
+        }
+    },
+    
+    stopSearch: function()
+    {
+        if (this._currentSearchScope)
+            this._currentSearchScope.stopSearch();
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.AdvancedSearchController} controller
+ */
+WebInspector.SearchView = function(controller)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("textEditor.css");
+    
+    this._controller = controller;
+
+    this.element.className = "search-view";
+
+    this._searchPanelElement = document.createElement("span");
+    this._searchPanelElement.className = "search-drawer-header";
+    this._searchPanelElement.addEventListener("keydown", this._onKeyDown.bind(this), false);
+    
+    this._searchResultsElement = this.element.createChild("div");
+    this._searchResultsElement.className = "search-results";
+    
+    this._searchLabel = this._searchPanelElement.createChild("span");
+    this._searchLabel.textContent = WebInspector.UIString("Search sources");
+    this._search = this._searchPanelElement.createChild("input");
+    this._search.setAttribute("type", "search");
+    this._search.addStyleClass("search-config-search");
+    this._search.setAttribute("results", "0");
+    this._search.setAttribute("size", 30);
+
+    this._ignoreCaseLabel = this._searchPanelElement.createChild("label");
+    this._ignoreCaseLabel.addStyleClass("search-config-label");
+    this._ignoreCaseCheckbox = this._ignoreCaseLabel.createChild("input");
+    this._ignoreCaseCheckbox.setAttribute("type", "checkbox");
+    this._ignoreCaseCheckbox.addStyleClass("search-config-checkbox");
+    this._ignoreCaseLabel.appendChild(document.createTextNode(WebInspector.UIString("Ignore case")));
+    
+    this._regexLabel = this._searchPanelElement.createChild("label");
+    this._regexLabel.addStyleClass("search-config-label");
+    this._regexCheckbox = this._regexLabel.createChild("input");
+    this._regexCheckbox.setAttribute("type", "checkbox");
+    this._regexCheckbox.addStyleClass("search-config-checkbox");
+    this._regexLabel.appendChild(document.createTextNode(WebInspector.UIString("Regular expression")));
+    
+    this._searchStatusBarElement = document.createElement("div");
+    this._searchStatusBarElement.className = "search-status-bar-item";
+    this._searchMessageElement = this._searchStatusBarElement.createChild("div");
+    this._searchMessageElement.className = "search-status-bar-message";
+
+    this._searchResultsMessageElement = document.createElement("span");
+    this._searchResultsMessageElement.className = "search-results-status-bar-message";
+
+    this._load();
+}
+
+// Number of recent search queries to store.
+WebInspector.SearchView.maxQueriesCount = 20;
+
+WebInspector.SearchView.prototype = {
+    /**
+     * @return {Array.<Element>}
+     */
+    get statusBarItems()
+    {
+        return [this._searchStatusBarElement, this._searchResultsMessageElement];
+    },
+
+    /**
+     * @return {WebInspector.SearchConfig}
+     */
+    get searchConfig()
+    {
+        return new WebInspector.SearchConfig(this._search.value, this._ignoreCaseCheckbox.checked, this._regexCheckbox.checked);
+    },
+
+    syncToSelection: function()
+    {
+        var selection = window.getSelection();
+        if (selection.rangeCount)
+            this._search.value = selection.toString().replace(/\r?\n.*/, "");
+    },
+    
+    /**
+     * @type {WebInspector.SearchResultsPane}
+     */
+    set resultsPane(resultsPane)
+    {
+        this.resetResults();
+        this._searchResultsElement.appendChild(resultsPane.element);
+    },
+    
+    /**
+     * @param {number} totalSearchResultsCount
+     */
+    searchStarted: function(totalSearchResultsCount)
+    {
+        this.resetResults();
+        this._resetCounters();
+
+        this._searchMessageElement.textContent = WebInspector.UIString("Searching...");
+        this._progressIndicator = new WebInspector.ProgressIndicator();
+        this._progressIndicator.setTotalWork(totalSearchResultsCount);
+        this._progressIndicator.show(this._searchStatusBarElement);
+        
+        this._updateSearchResultsMessage();
+        
+        if (!this._searchingView)
+            this._searchingView = new WebInspector.EmptyView(WebInspector.UIString("Searching..."));
+        this._searchingView.show(this._searchResultsElement);
+    },
+
+    _updateSearchResultsMessage: function()
+    {
+        if (this._searchMatchesCount && this._searchResultsCount)
+            this._searchResultsMessageElement.textContent = WebInspector.UIString("Found %d matches in %d files.", this._searchMatchesCount, this._nonEmptySearchResultsCount);
+        else
+            this._searchResultsMessageElement.textContent = "";
+    },
+
+    resetResults: function()
+    {
+        if (this._searchingView)
+            this._searchingView.detach();
+        if (this._notFoundView)
+            this._notFoundView.detach();
+        this._searchResultsElement.removeChildren();
+    },
+
+    _resetCounters: function()
+    {
+        this._searchMatchesCount = 0;
+        this._searchResultsCount = 0;
+        this._nonEmptySearchResultsCount = 0;
+    },
+
+    nothingFound: function()
+    {
+        this.resetResults();
+
+        if (!this._notFoundView)
+            this._notFoundView = new WebInspector.EmptyView(WebInspector.UIString("No matches found."));
+        this._notFoundView.show(this._searchResultsElement);
+        this._searchResultsMessageElement.textContent = WebInspector.UIString("No matches found.");
+    },
+
+    /**
+     * @param {WebInspector.FileBasedSearchResultsPane.SearchResult} searchResult
+     */
+    addSearchResult: function(searchResult)
+    {
+        this._searchMatchesCount += searchResult.searchMatches.length;
+        this._searchResultsCount++;
+        if (searchResult.searchMatches.length)
+            this._nonEmptySearchResultsCount++;
+        this._updateSearchResultsMessage();
+        if (this._progressIndicator.isCanceled())
+            this._onCancel();
+        else
+            this._progressIndicator.setWorked(this._searchResultsCount);
+    },
+
+    /**
+     * @param {boolean} finished
+     */
+    searchFinished: function(finished)
+    {
+        this._progressIndicator.done();
+        this._searchMessageElement.textContent = finished ? WebInspector.UIString("Search finished.") : WebInspector.UIString("Search interrupted.");
+    },
+
+    focus: function()
+    {
+        WebInspector.setCurrentFocusElement(this._search);
+        this._search.select();
+    },
+
+    wasShown: function()
+    {
+        this.focus();
+    },
+
+    willHide: function()
+    {
+        this._controller.stopSearch();
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onKeyDown: function(event)
+    {
+        switch (event.keyCode) {
+        case WebInspector.KeyboardShortcut.Keys.Enter.code:
+            this._onAction();
+            break;
+        case WebInspector.KeyboardShortcut.Keys.Esc.code:
+            this._controller.close();
+            event.consume(true);
+            break;
+        }        
+    },
+    
+    _save: function()
+    {
+        var searchConfig = new WebInspector.SearchConfig(this.searchConfig.query, this.searchConfig.ignoreCase, this.searchConfig.isRegex); 
+        WebInspector.settings.advancedSearchConfig.set(searchConfig);
+    },
+    
+    _load: function()
+    {
+        var searchConfig = WebInspector.settings.advancedSearchConfig.get();
+        this._search.value = searchConfig.query;
+        this._ignoreCaseCheckbox.checked = searchConfig.ignoreCase;
+        this._regexCheckbox.checked = searchConfig.isRegex;
+    },
+
+    _onCancel: function()
+    {
+        this._controller.stopSearch();
+        this.focus();
+    },
+    
+    _onAction: function()
+    {
+        if (!this.searchConfig.query || !this.searchConfig.query.length)
+            return;
+        
+        this._save();
+        this._controller.startSearch(this.searchConfig);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+
+/**
+ * @constructor
+ * @param {string} query
+ * @param {boolean} ignoreCase
+ * @param {boolean} isRegex
+ */
+WebInspector.SearchConfig = function(query, ignoreCase, isRegex)
+{
+    this.query = query;
+    this.ignoreCase = ignoreCase;
+    this.isRegex = isRegex;
+}
+
+/**
+ * @interface
+ */
+WebInspector.SearchScope = function()
+{
+}
+
+WebInspector.SearchScope.prototype = {
+    /**
+     * @param {WebInspector.SearchConfig} searchConfig
+     * @param {function(WebInspector.FileBasedSearchResultsPane.SearchResult)} searchResultCallback
+     * @param {function(boolean)} searchFinishedCallback
+     */
+    performSearch: function(searchConfig, searchResultCallback, searchFinishedCallback) { },
+
+    stopSearch: function() { },
+    
+    /**
+     * @param {WebInspector.SearchConfig} searchConfig
+     * @return {WebInspector.SearchResultsPane}
+     */
+    createSearchResultsPane: function(searchConfig) { }
+}
+
+/**
+ * @constructor
+ * @param {number} offset
+ * @param {number} length
+ */
+WebInspector.SearchResult = function(offset, length)
+{
+    this.offset = offset;
+    this.length = length;    
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.SearchConfig} searchConfig
+ */
+WebInspector.SearchResultsPane = function(searchConfig)
+{
+    this._searchConfig = searchConfig;
+    this.element = document.createElement("div");
+}
+
+WebInspector.SearchResultsPane.prototype = {
+    /**
+     * @return {WebInspector.SearchConfig}
+     */
+    get searchConfig()
+    {
+        return this._searchConfig;
+    },
+
+    /**
+     * @param {WebInspector.FileBasedSearchResultsPane.SearchResult} searchResult
+     */
+    addSearchResult: function(searchResult) { }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SearchResultsPane} 
+ * @param {WebInspector.SearchConfig} searchConfig
+ */
+WebInspector.FileBasedSearchResultsPane = function(searchConfig)
+{
+    WebInspector.SearchResultsPane.call(this, searchConfig);
+    
+    this._searchResults = [];
+
+    this.element.id ="search-results-pane-file-based";
+    
+    this._treeOutlineElement = document.createElement("ol");
+    this._treeOutlineElement.className = "search-results-outline-disclosure";
+    this.element.appendChild(this._treeOutlineElement);
+    this._treeOutline = new TreeOutline(this._treeOutlineElement);
+    
+    this._matchesExpandedCount = 0;
+}
+
+WebInspector.FileBasedSearchResultsPane.matchesExpandedByDefaultCount = 20;
+WebInspector.FileBasedSearchResultsPane.fileMatchesShownAtOnce = 20;
+
+WebInspector.FileBasedSearchResultsPane.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {Element}
+     */
+    _createAnchor: function(uiSourceCode, lineNumber, columnNumber)
+    {
+        var anchor = document.createElement("a");
+        anchor.preferredPanel = "scripts";
+        anchor.href = sanitizeHref(uiSourceCode.originURL());
+        anchor.uiSourceCode = uiSourceCode;
+        anchor.lineNumber = lineNumber;
+        return anchor;
+    },
+
+    /**
+     * @param {WebInspector.FileBasedSearchResultsPane.SearchResult} searchResult
+     */
+    addSearchResult: function(searchResult)
+    {
+        this._searchResults.push(searchResult);
+        var uiSourceCode = searchResult.uiSourceCode;
+        var searchMatches = searchResult.searchMatches;
+
+        var fileTreeElement = this._addFileTreeElement(uiSourceCode.originURL(), searchMatches.length, this._searchResults.length - 1);
+    },
+
+    /**
+     * @param {WebInspector.FileBasedSearchResultsPane.SearchResult} searchResult
+     * @param {TreeElement} fileTreeElement
+     */
+    _fileTreeElementExpanded: function(searchResult, fileTreeElement)
+    {
+        if (fileTreeElement._initialized)
+            return;
+        
+        var toIndex = Math.min(searchResult.searchMatches.length, WebInspector.FileBasedSearchResultsPane.fileMatchesShownAtOnce);
+        if (toIndex < searchResult.searchMatches.length) {
+            this._appendSearchMatches(fileTreeElement, searchResult, 0, toIndex - 1);
+            this._appendShowMoreMatchesElement(fileTreeElement, searchResult, toIndex - 1);
+        } else
+            this._appendSearchMatches(fileTreeElement, searchResult, 0, toIndex);
+        
+        fileTreeElement._initialized = true;
+    },
+
+    /**
+     * @param {TreeElement} fileTreeElement
+     * @param {WebInspector.FileBasedSearchResultsPane.SearchResult} searchResult
+     * @param {number} fromIndex
+     * @param {number} toIndex
+     */
+    _appendSearchMatches: function(fileTreeElement, searchResult, fromIndex, toIndex)
+    {
+        var uiSourceCode = searchResult.uiSourceCode;
+        var searchMatches = searchResult.searchMatches;
+
+        var regex = createSearchRegex(this._searchConfig.query, !this._searchConfig.ignoreCase, this._searchConfig.isRegex);
+        for (var i = fromIndex; i < toIndex; ++i) {
+            var lineNumber = searchMatches[i].lineNumber;
+            var lineContent = searchMatches[i].lineContent;
+            var matchRanges = this._regexMatchRanges(lineContent, regex);
+            
+            var anchor = this._createAnchor(uiSourceCode, lineNumber, matchRanges[0].offset);
+            
+            var numberString = numberToStringWithSpacesPadding(lineNumber + 1, 4);
+            var lineNumberSpan = document.createElement("span");
+            lineNumberSpan.addStyleClass("webkit-line-number");
+            lineNumberSpan.addStyleClass("search-match-line-number");
+            lineNumberSpan.textContent = numberString;
+            anchor.appendChild(lineNumberSpan);
+            
+            var contentSpan = this._createContentSpan(lineContent, matchRanges);
+            anchor.appendChild(contentSpan);
+            
+            var searchMatchElement = new TreeElement("", null, false);
+            fileTreeElement.appendChild(searchMatchElement);
+            searchMatchElement.listItemElement.className = "search-match source-code";
+            searchMatchElement.listItemElement.appendChild(anchor);
+        }
+    },
+
+    /**
+     * @param {TreeElement} fileTreeElement
+     * @param {WebInspector.FileBasedSearchResultsPane.SearchResult} searchResult
+     * @param {number} startMatchIndex
+     */
+    _appendShowMoreMatchesElement: function(fileTreeElement, searchResult, startMatchIndex)
+    {
+        var matchesLeftCount = searchResult.searchMatches.length - startMatchIndex;
+        var showMoreMatchesText = WebInspector.UIString("Show all matches (%d more).", matchesLeftCount);
+        var showMoreMatchesElement = new TreeElement(showMoreMatchesText, null, false);
+        fileTreeElement.appendChild(showMoreMatchesElement);
+        showMoreMatchesElement.listItemElement.addStyleClass("show-more-matches");
+        showMoreMatchesElement.onselect = this._showMoreMatchesElementSelected.bind(this, searchResult, startMatchIndex, showMoreMatchesElement);
+    },
+
+    /**
+     * @param {WebInspector.FileBasedSearchResultsPane.SearchResult} searchResult
+     * @param {number} startMatchIndex
+     * @param {TreeElement} showMoreMatchesElement
+     */
+    _showMoreMatchesElementSelected: function(searchResult, startMatchIndex, showMoreMatchesElement)
+    {
+        var fileTreeElement = showMoreMatchesElement.parent;
+        fileTreeElement.removeChild(showMoreMatchesElement);
+        this._appendSearchMatches(fileTreeElement, searchResult, startMatchIndex, searchResult.searchMatches.length);
+    },
+
+    /**
+     * @param {string} fileName
+     * @param {number} searchMatchesCount
+     * @param {number} searchResultIndex
+     */
+    _addFileTreeElement: function(fileName, searchMatchesCount, searchResultIndex)
+    {
+        var fileTreeElement = new TreeElement("", null, true);
+        fileTreeElement.toggleOnClick = true;
+        fileTreeElement.selectable = false;
+
+        this._treeOutline.appendChild(fileTreeElement);
+        fileTreeElement.listItemElement.addStyleClass("search-result");
+
+        var fileNameSpan = document.createElement("span");
+        fileNameSpan.className = "search-result-file-name";
+        fileNameSpan.textContent = fileName;
+        fileTreeElement.listItemElement.appendChild(fileNameSpan);
+
+        var matchesCountSpan = document.createElement("span");
+        matchesCountSpan.className = "search-result-matches-count";
+        if (searchMatchesCount === 1)
+            matchesCountSpan.textContent = WebInspector.UIString("(%d match)", searchMatchesCount);
+        else
+            matchesCountSpan.textContent = WebInspector.UIString("(%d matches)", searchMatchesCount);
+        
+        fileTreeElement.listItemElement.appendChild(matchesCountSpan);
+        
+        var searchResult = this._searchResults[searchResultIndex];
+        fileTreeElement.onexpand = this._fileTreeElementExpanded.bind(this, searchResult, fileTreeElement);
+
+        // Expand until at least certain amount of matches is expanded.
+        if (this._matchesExpandedCount < WebInspector.FileBasedSearchResultsPane.matchesExpandedByDefaultCount)
+            fileTreeElement.expand();
+        this._matchesExpandedCount += searchResult.searchMatches.length;
+
+        return fileTreeElement; 
+    },
+
+    /**
+     * @param {string} lineContent
+     * @param {RegExp} regex
+     * @return {Array.<WebInspector.SearchResult>}
+     */
+    _regexMatchRanges: function(lineContent, regex)
+    {
+        regex.lastIndex = 0;
+        var match;
+        var offset = 0;
+        var matchRanges = [];
+        while ((regex.lastIndex < lineContent.length) && (match = regex.exec(lineContent)))
+            matchRanges.push(new WebInspector.SearchResult(match.index, match[0].length));
+
+        return matchRanges;
+    },
+    
+    /**
+     * @param {string} lineContent
+     * @param {Array.<WebInspector.SearchResult>} matchRanges
+     */
+    _createContentSpan: function(lineContent, matchRanges)
+    {
+        var contentSpan = document.createElement("span");
+        contentSpan.className = "search-match-content";
+        contentSpan.textContent = lineContent;
+        WebInspector.highlightRangesWithStyleClass(contentSpan, matchRanges, "highlighted-match");
+        return contentSpan;
+    },
+
+    __proto__: WebInspector.SearchResultsPane.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ * @param {Array.<Object>} searchMatches
+ */
+WebInspector.FileBasedSearchResultsPane.SearchResult = function(uiSourceCode, searchMatches) {
+    this.uiSourceCode = uiSourceCode;
+    this.searchMatches = searchMatches;
+}
+
+/**
+ * @type {WebInspector.AdvancedSearchController}
+ */
+WebInspector.advancedSearchController = null;
diff --git a/Source/devtools/front_end/ApplicationCacheItemsView.js b/Source/devtools/front_end/ApplicationCacheItemsView.js
new file mode 100644
index 0000000..630fd6d
--- /dev/null
+++ b/Source/devtools/front_end/ApplicationCacheItemsView.js
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.ApplicationCacheItemsView = function(model, frameId)
+{
+    WebInspector.View.call(this);
+    
+    this._model = model;
+
+    this.element.addStyleClass("storage-view");
+    this.element.addStyleClass("table");
+
+    // FIXME: Needs better tooltip. (Localized)
+    this.deleteButton = new WebInspector.StatusBarButton(WebInspector.UIString("Delete"), "delete-storage-status-bar-item");
+    this.deleteButton.visible = false;
+    this.deleteButton.addEventListener("click", this._deleteButtonClicked, this);
+
+    this.connectivityIcon = document.createElement("div");
+    this.connectivityMessage = document.createElement("span");
+    this.connectivityMessage.className = "storage-application-cache-connectivity";
+    this.connectivityMessage.textContent = "";
+
+    this.divider = document.createElement("span");
+    this.divider.className = "status-bar-item status-bar-divider";
+
+    this.statusIcon = document.createElement("div");
+    this.statusMessage = document.createElement("span");
+    this.statusMessage.className = "storage-application-cache-status";
+    this.statusMessage.textContent = "";
+
+    this._frameId = frameId;
+
+    this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("No Application Cache information available."));
+    this._emptyView.show(this.element);
+
+    this._markDirty();
+    
+    var status = this._model.frameManifestStatus(frameId);
+    this.updateStatus(status);
+    
+    this.updateNetworkState(this._model.onLine);
+
+    // FIXME: Status bar items don't work well enough yet, so they are being hidden.
+    // http://webkit.org/b/41637 Web Inspector: Give Semantics to "Refresh" and "Delete" Buttons in ApplicationCache DataGrid
+    this.deleteButton.element.style.display = "none";
+}
+
+WebInspector.ApplicationCacheItemsView.prototype = {
+    get statusBarItems()
+    {
+        return [
+            this.deleteButton.element,
+            this.connectivityIcon, this.connectivityMessage, this.divider,
+            this.statusIcon, this.statusMessage
+        ];
+    },
+
+    wasShown: function()
+    {
+        this._maybeUpdate();
+    },
+
+    willHide: function()
+    {
+        this.deleteButton.visible = false;
+    },
+
+    _maybeUpdate: function()
+    {
+        if (!this.isShowing() || !this._viewDirty)
+            return;
+        
+        this._update();
+        this._viewDirty = false;
+    },
+
+    _markDirty: function()
+    {
+        this._viewDirty = true;
+    },
+
+    /**
+     * @param {number} status
+     */
+    updateStatus: function(status)
+    {
+        var oldStatus = this._status;
+        this._status = status;
+        
+        var statusInformation = {};
+        // We should never have UNCACHED status, since we remove frames with UNCACHED application cache status from the tree. 
+        statusInformation[applicationCache.UNCACHED]    = { className: "red-ball", text: "UNCACHED" };
+        statusInformation[applicationCache.IDLE]        = { className: "green-ball", text: "IDLE" };
+        statusInformation[applicationCache.CHECKING]    = { className: "orange-ball",  text: "CHECKING" };
+        statusInformation[applicationCache.DOWNLOADING] = { className: "orange-ball",  text: "DOWNLOADING" };
+        statusInformation[applicationCache.UPDATEREADY] = { className: "green-ball",  text: "UPDATEREADY" };
+        statusInformation[applicationCache.OBSOLETE]    = { className: "red-ball",      text: "OBSOLETE" };
+
+        var info = statusInformation[status] || statusInformation[applicationCache.UNCACHED];
+
+        this.statusIcon.className = "storage-application-cache-status-icon " + info.className;
+        this.statusMessage.textContent = info.text;
+
+        if (this.isShowing() && this._status === applicationCache.IDLE && (oldStatus === applicationCache.UPDATEREADY || !this._resources))
+            this._markDirty();
+        this._maybeUpdate();
+    },
+
+    /**
+     * @param {boolean} isNowOnline
+     */
+    updateNetworkState: function(isNowOnline)
+    {
+        if (isNowOnline) {
+            this.connectivityIcon.className = "storage-application-cache-connectivity-icon green-ball";
+            this.connectivityMessage.textContent = WebInspector.UIString("Online");
+        } else {
+            this.connectivityIcon.className = "storage-application-cache-connectivity-icon red-ball";
+            this.connectivityMessage.textContent = WebInspector.UIString("Offline");
+        }
+    },
+
+    _update: function()
+    {
+        this._model.requestApplicationCache(this._frameId, this._updateCallback.bind(this));
+    },
+
+    /**
+     * @param {Object} applicationCache
+     */
+    _updateCallback: function(applicationCache)
+    {
+        if (!applicationCache || !applicationCache.manifestURL) {
+            delete this._manifest;
+            delete this._creationTime;
+            delete this._updateTime;
+            delete this._size;
+            delete this._resources;
+            
+            this._emptyView.show(this.element);
+            this.deleteButton.visible = false;
+            if (this._dataGrid)
+                this._dataGrid.element.addStyleClass("hidden");
+            return;
+        }
+        // FIXME: are these variables needed anywhere else?
+        this._manifest = applicationCache.manifestURL;
+        this._creationTime = applicationCache.creationTime;
+        this._updateTime = applicationCache.updateTime;
+        this._size = applicationCache.size;
+        this._resources = applicationCache.resources;
+
+        if (!this._dataGrid)
+            this._createDataGrid();
+
+        this._populateDataGrid();
+        this._dataGrid.autoSizeColumns(20, 80);
+        this._dataGrid.element.removeStyleClass("hidden");
+        this._emptyView.detach();
+        this.deleteButton.visible = true;
+
+        // FIXME: For Chrome, put creationTime and updateTime somewhere.
+        // NOTE: localizedString has not yet been added.
+        // WebInspector.UIString("(%s) Created: %s Updated: %s", this._size, this._creationTime, this._updateTime);
+    },
+
+    _createDataGrid: function()
+    {
+        var columns = [
+            {title: WebInspector.UIString("Resource"), sort: WebInspector.DataGrid.Order.Ascending, sortable: true},
+            {title: WebInspector.UIString("Type"), sortable: true},
+            {title: WebInspector.UIString("Size"), align: WebInspector.DataGrid.Align.Right, sortable: true}
+        ];
+        this._dataGrid = new WebInspector.DataGrid(columns);
+        this._dataGrid.show(this.element);
+        this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._populateDataGrid, this);
+    },
+
+    _populateDataGrid: function()
+    {
+        var selectedResource = this._dataGrid.selectedNode ? this._dataGrid.selectedNode.resource : null;
+        var sortDirection = this._dataGrid.isSortOrderAscending() ? 1 : -1;
+
+        function numberCompare(field, resource1, resource2)
+        {
+            return sortDirection * (resource1[field] - resource2[field]);
+        }
+        function localeCompare(field, resource1, resource2)
+        {
+             return sortDirection * (resource1[field] + "").localeCompare(resource2[field] + "")
+        }
+
+        var comparator;
+        switch (parseInt(this._dataGrid.sortColumnIdentifier(), 10)) {
+            case 0: comparator = localeCompare.bind(this, "name"); break;
+            case 1: comparator = localeCompare.bind(this, "type"); break;
+            case 2: comparator = numberCompare.bind(this, "size"); break;
+            default: localeCompare.bind(this, "resource"); // FIXME: comparator = ?
+        }
+
+        this._resources.sort(comparator);
+        this._dataGrid.rootNode().removeChildren();
+
+        var nodeToSelect;
+        for (var i = 0; i < this._resources.length; ++i) {
+            var data = {};
+            var resource = this._resources[i];
+            data[0] = resource.url;
+            data[1] = resource.type;
+            data[2] = Number.bytesToString(resource.size);
+            var node = new WebInspector.DataGridNode(data);
+            node.resource = resource;
+            node.selectable = true;
+            this._dataGrid.rootNode().appendChild(node);
+            if (resource === selectedResource) {
+                nodeToSelect = node;
+                nodeToSelect.selected = true;
+            }
+        }
+
+        if (!nodeToSelect && this._dataGrid.rootNode().children.length)
+            this._dataGrid.rootNode().children[0].selected = true;
+    },
+
+    _deleteButtonClicked: function(event)
+    {
+        if (!this._dataGrid || !this._dataGrid.selectedNode)
+            return;
+
+        // FIXME: Delete Button semantics are not yet defined. (Delete a single, or all?)
+        this._deleteCallback(this._dataGrid.selectedNode);
+    },
+
+    _deleteCallback: function(node)
+    {
+        // FIXME: Should we delete a single (selected) resource or all resources?
+        // InspectorBackend.deleteCachedResource(...)
+        // this._update();
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
diff --git a/Source/devtools/front_end/ApplicationCacheModel.js b/Source/devtools/front_end/ApplicationCacheModel.js
new file mode 100644
index 0000000..7f40830
--- /dev/null
+++ b/Source/devtools/front_end/ApplicationCacheModel.js
@@ -0,0 +1,253 @@
+/*
+ * 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.ApplicationCacheModel = function()
+{
+    ApplicationCacheAgent.enable();
+    InspectorBackend.registerApplicationCacheDispatcher(new WebInspector.ApplicationCacheDispatcher(this));
+    
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, this._frameNavigated, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this._frameDetached, this);
+    
+    this._statuses = {};
+    this._manifestURLsByFrame = {};
+
+    this._mainFrameNavigated();
+    
+    this._onLine = true;
+}
+
+WebInspector.ApplicationCacheModel.EventTypes = {
+    FrameManifestStatusUpdated: "FrameManifestStatusUpdated",
+    FrameManifestAdded: "FrameManifestAdded",
+    FrameManifestRemoved: "FrameManifestRemoved",
+    NetworkStateChanged: "NetworkStateChanged"
+}
+
+WebInspector.ApplicationCacheModel.prototype = {
+    _frameNavigated: function(event)
+    {
+        var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data);
+        if (frame.isMainFrame()) {
+            this._mainFrameNavigated();
+            return;
+        }
+
+        ApplicationCacheAgent.getManifestForFrame(frame.id, this._manifestForFrameLoaded.bind(this, frame.id));
+    },
+    
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _frameDetached: function(event)
+    {
+        var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data);
+        this._frameManifestRemoved(frame.id);
+    },
+    
+    _mainFrameNavigated: function()
+    {
+        ApplicationCacheAgent.getFramesWithManifests(this._framesWithManifestsLoaded.bind(this));
+    },
+
+    /**
+     * @param {string} frameId
+     * @param {?Protocol.Error} error
+     * @param {string} manifestURL
+     */
+    _manifestForFrameLoaded: function(frameId, error, manifestURL)
+    {
+        if (error) {
+            console.error(error);
+            return;
+        }
+        
+        if (!manifestURL)
+            this._frameManifestRemoved(frameId);
+    },
+    
+    /**
+     * @param {?Protocol.Error} error
+     * @param {Array.<ApplicationCacheAgent.FrameWithManifest>} framesWithManifests
+     */
+    _framesWithManifestsLoaded: function(error, framesWithManifests)
+    {
+        if (error) {
+            console.error(error);
+            return;
+        }
+
+        for (var i = 0; i < framesWithManifests.length; ++i)
+            this._frameManifestUpdated(framesWithManifests[i].frameId, framesWithManifests[i].manifestURL, framesWithManifests[i].status);
+    },
+    
+    /**
+     * @param {string} frameId
+     * @param {string} manifestURL
+     * @param {number} status
+     */
+    _frameManifestUpdated: function(frameId, manifestURL, status)
+    {
+        if (status === applicationCache.UNCACHED) {
+            this._frameManifestRemoved(frameId);
+            return;
+        }
+            
+        if (!manifestURL)
+            return;
+            
+        if (this._manifestURLsByFrame[frameId] && manifestURL !== this._manifestURLsByFrame[frameId])
+            this._frameManifestRemoved(frameId);
+        
+        var statusChanged = this._statuses[frameId] !== status;
+        this._statuses[frameId] = status;
+        
+        if (!this._manifestURLsByFrame[frameId]) {
+            this._manifestURLsByFrame[frameId] = manifestURL;
+            this.dispatchEventToListeners(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestAdded, frameId);
+        }
+            
+        if (statusChanged)
+            this.dispatchEventToListeners(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestStatusUpdated, frameId);
+    },
+    
+    /**
+     * @param {string} frameId
+     */
+    _frameManifestRemoved: function(frameId)
+    {
+        if (!this._manifestURLsByFrame[frameId])
+            return;
+
+        var manifestURL = this._manifestURLsByFrame[frameId];
+        delete this._manifestURLsByFrame[frameId];
+        delete this._statuses[frameId];
+        
+        this.dispatchEventToListeners(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestRemoved, frameId);
+    },
+    
+    /**
+     * @param {string} frameId
+     * @return {string}
+     */
+    frameManifestURL: function(frameId)
+    {
+        return this._manifestURLsByFrame[frameId] || "";
+    },
+    
+    /**
+     * @param {string} frameId
+     * @return {number}
+     */
+    frameManifestStatus: function(frameId)
+    {
+        return this._statuses[frameId] || applicationCache.UNCACHED;
+    },
+    
+    /**
+     * @return {boolean}
+     */
+    get onLine()
+    {
+        return this._onLine;
+    },
+    
+    /**
+     * @param {string} frameId
+     * @param {string} manifestURL
+     * @param {number} status
+     */
+    _statusUpdated: function(frameId, manifestURL, status)
+    {
+        this._frameManifestUpdated(frameId, manifestURL, status);
+    },
+    
+    /**
+     * @param {string} frameId
+     * @param {function(Object)} callback
+     */
+    requestApplicationCache: function(frameId, callback)
+    {
+        function callbackWrapper(error, applicationCache)
+        {
+            if (error) {
+                console.error(error);
+                callback(null);
+                return;
+            }
+            
+            callback(applicationCache);
+        }
+        
+        ApplicationCacheAgent.getApplicationCacheForFrame(frameId, callbackWrapper.bind(this));
+    },
+    
+    /**
+     * @param {boolean} isNowOnline
+     */
+    _networkStateUpdated: function(isNowOnline)
+    {
+        this._onLine = isNowOnline;
+        this.dispatchEventToListeners(WebInspector.ApplicationCacheModel.EventTypes.NetworkStateChanged, isNowOnline);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {ApplicationCacheAgent.Dispatcher}
+ */
+WebInspector.ApplicationCacheDispatcher = function(applicationCacheModel)
+{
+    this._applicationCacheModel = applicationCacheModel;
+}
+
+WebInspector.ApplicationCacheDispatcher.prototype = {
+    /**
+     * @param {string} frameId
+     * @param {string} manifestURL
+     * @param {number} status
+     */
+    applicationCacheStatusUpdated: function(frameId, manifestURL, status)
+    {
+        this._applicationCacheModel._statusUpdated(frameId, manifestURL, status);
+    },
+    
+    /**
+     * @param {boolean} isNowOnline
+     */
+    networkStateUpdated: function(isNowOnline)
+    {
+        this._applicationCacheModel._networkStateUpdated(isNowOnline);
+    }
+}
diff --git a/Source/devtools/front_end/AuditCategories.js b/Source/devtools/front_end/AuditCategories.js
new file mode 100644
index 0000000..ffd3f63
--- /dev/null
+++ b/Source/devtools/front_end/AuditCategories.js
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditCategory}
+ */
+WebInspector.AuditCategories.PagePerformance = function() {
+    WebInspector.AuditCategory.call(this, WebInspector.AuditCategories.PagePerformance.AuditCategoryName);
+}
+
+WebInspector.AuditCategories.PagePerformance.AuditCategoryName = "Web Page Performance";
+
+WebInspector.AuditCategories.PagePerformance.prototype = {
+    initialize: function()
+    {
+        this.addRule(new WebInspector.AuditRules.UnusedCssRule(), WebInspector.AuditRule.Severity.Warning);
+        this.addRule(new WebInspector.AuditRules.CssInHeadRule(), WebInspector.AuditRule.Severity.Severe);
+        this.addRule(new WebInspector.AuditRules.StylesScriptsOrderRule(), WebInspector.AuditRule.Severity.Severe);
+        this.addRule(new WebInspector.AuditRules.VendorPrefixedCSSProperties(), WebInspector.AuditRule.Severity.Warning);
+    },
+
+    __proto__: WebInspector.AuditCategory.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditCategory}
+ */
+WebInspector.AuditCategories.NetworkUtilization = function() {
+    WebInspector.AuditCategory.call(this, WebInspector.AuditCategories.NetworkUtilization.AuditCategoryName);
+}
+
+WebInspector.AuditCategories.NetworkUtilization.AuditCategoryName = "Network Utilization";
+
+WebInspector.AuditCategories.NetworkUtilization.prototype = {
+    initialize: function()
+    {
+        this.addRule(new WebInspector.AuditRules.GzipRule(), WebInspector.AuditRule.Severity.Severe);
+        this.addRule(new WebInspector.AuditRules.ImageDimensionsRule(), WebInspector.AuditRule.Severity.Warning);
+        this.addRule(new WebInspector.AuditRules.CookieSizeRule(400), WebInspector.AuditRule.Severity.Warning);
+        this.addRule(new WebInspector.AuditRules.StaticCookielessRule(5), WebInspector.AuditRule.Severity.Warning);
+        this.addRule(new WebInspector.AuditRules.CombineJsResourcesRule(2), WebInspector.AuditRule.Severity.Severe);
+        this.addRule(new WebInspector.AuditRules.CombineCssResourcesRule(2), WebInspector.AuditRule.Severity.Severe);
+        this.addRule(new WebInspector.AuditRules.MinimizeDnsLookupsRule(4), WebInspector.AuditRule.Severity.Warning);
+        this.addRule(new WebInspector.AuditRules.ParallelizeDownloadRule(4, 10, 0.5), WebInspector.AuditRule.Severity.Warning);
+        this.addRule(new WebInspector.AuditRules.BrowserCacheControlRule(), WebInspector.AuditRule.Severity.Severe);
+        this.addRule(new WebInspector.AuditRules.ProxyCacheControlRule(), WebInspector.AuditRule.Severity.Warning);
+    },
+
+    __proto__: WebInspector.AuditCategory.prototype
+}
diff --git a/Source/devtools/front_end/AuditController.js b/Source/devtools/front_end/AuditController.js
new file mode 100644
index 0000000..36748f2
--- /dev/null
+++ b/Source/devtools/front_end/AuditController.js
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Samsung Electronics. 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.AuditsPanel} auditsPanel
+ */
+WebInspector.AuditController = function(auditsPanel)
+{
+    this._auditsPanel = auditsPanel;
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.OnLoad, this._didMainResourceLoad, this);
+}
+
+WebInspector.AuditController.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.AuditCategory>} categories
+     * @param {function(string, !Array.<!WebInspector.AuditCategoryResult>)} resultCallback
+     */
+    _executeAudit: function(categories, resultCallback)
+    {
+        this._progress.setTitle(WebInspector.UIString("Running audit"));
+
+        function ruleResultReadyCallback(categoryResult, ruleResult)
+        {
+            if (ruleResult && ruleResult.children)
+                categoryResult.addRuleResult(ruleResult);
+
+            if (this._progress.isCanceled())
+                this._progress.done();
+        }
+
+        var results = [];
+        var mainResourceURL = WebInspector.inspectedPageURL;
+        var categoriesDone = 0;
+        function categoryDoneCallback()
+        {
+            if (++categoriesDone !== categories.length)
+                return;
+            this._progress.done();
+            resultCallback(mainResourceURL, results)
+        }
+
+        var requests = WebInspector.networkLog.requests.slice();
+        var compositeProgress = new WebInspector.CompositeProgress(this._progress);
+        var subprogresses = [];
+        for (var i = 0; i < categories.length; ++i)
+            subprogresses.push(compositeProgress.createSubProgress());
+        for (var i = 0; i < categories.length; ++i) {
+            var category = categories[i];
+            var result = new WebInspector.AuditCategoryResult(category);
+            results.push(result);
+            category.run(requests, ruleResultReadyCallback.bind(this, result), categoryDoneCallback.bind(this), subprogresses[i]);
+        }
+    },
+
+    /**
+     * @param {function()} launcherCallback
+     * @param {string} mainResourceURL
+     * @param {!Array.<!WebInspector.AuditCategoryResult>} results
+     */
+    _auditFinishedCallback: function(launcherCallback, mainResourceURL, results)
+    {
+        this._auditsPanel.auditFinishedCallback(mainResourceURL, results);
+        if (!this._progress.isCanceled())
+            launcherCallback();
+    },
+
+    /**
+     * @param {Array.<string>} categoryIds
+     * @param {WebInspector.Progress} progress
+     * @param {boolean} runImmediately
+     * @param {function()} startedCallback
+     * @param {function()} finishedCallback
+     */
+    initiateAudit: function(categoryIds, progress, runImmediately, startedCallback, finishedCallback)
+    {
+        if (!categoryIds || !categoryIds.length)
+            return;
+
+        this._progress = progress;
+
+        var categories = [];
+        for (var i = 0; i < categoryIds.length; ++i)
+            categories.push(this._auditsPanel.categoriesById[categoryIds[i]]);
+
+        function startAuditWhenResourcesReady()
+        {
+            startedCallback();
+            this._executeAudit(categories, this._auditFinishedCallback.bind(this, finishedCallback));
+        }
+
+        if (runImmediately)
+            startAuditWhenResourcesReady.call(this);
+        else
+            this._reloadResources(startAuditWhenResourcesReady.bind(this));
+
+        WebInspector.userMetrics.AuditsStarted.record();
+    },
+
+    /**
+     * @param {function()=} callback
+     */
+    _reloadResources: function(callback)
+    {
+        this._pageReloadCallback = callback;
+        PageAgent.reload(false);
+    },
+
+    _didMainResourceLoad: function()
+    {
+        if (this._pageReloadCallback) {
+            var callback = this._pageReloadCallback;
+            delete this._pageReloadCallback;
+            callback();
+        }
+    }
+}
diff --git a/Source/devtools/front_end/AuditFormatters.js b/Source/devtools/front_end/AuditFormatters.js
new file mode 100644
index 0000000..efb0485
--- /dev/null
+++ b/Source/devtools/front_end/AuditFormatters.js
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.AuditFormatters = function()
+{
+}
+
+WebInspector.AuditFormatters.Registry = {
+    text: function(text)
+    {
+        return document.createTextNode(text);
+    },
+
+    snippet: function(snippetText)
+    {
+        var div = document.createElement("div");
+        div.textContent = snippetText;
+        div.className = "source-code";
+        return div;
+    },
+
+    concat: function()
+    {
+        var parent = document.createElement("span");
+        for (var arg = 0; arg < arguments.length; ++arg)
+            parent.appendChild(WebInspector.auditFormatters.apply(arguments[arg]));
+        return parent;
+    },
+
+    url: function(url, displayText, allowExternalNavigation)
+    {
+        var a = document.createElement("a");
+        a.href = sanitizeHref(url);
+        a.title = url;
+        a.textContent = displayText || url;
+        if (allowExternalNavigation)
+            a.target = "_blank";
+        return a;
+    },
+
+    resourceLink: function(url, line)
+    {
+        // FIXME: use WebInspector.Linkifier
+        return WebInspector.linkifyResourceAsNode(url, line, "console-message-url webkit-html-resource-link");
+    }
+};
+
+WebInspector.AuditFormatters.prototype = {
+    /**
+     * @param {string|boolean|number|Object} value
+     */
+    apply: function(value)
+    {
+        var formatter;
+        var type = typeof value;
+        var args;
+
+        switch (type) {
+        case "string":
+        case "boolean":
+        case "number":
+            formatter = WebInspector.AuditFormatters.Registry.text;
+        args = [ value.toString() ];
+        break;
+
+        case "object":
+            if (value instanceof Node)
+                return value;
+            if (value instanceof Array) {
+                formatter = WebInspector.AuditFormatters.Registry.concat;
+                args = value;
+            } else if (value.type && value.arguments) {
+                formatter = WebInspector.AuditFormatters.Registry[value.type];
+                args = value.arguments;
+            }
+        }
+        if (!formatter)
+            throw "Invalid value or formatter: " + type + JSON.stringify(value);
+
+        return formatter.apply(null, args);
+    },
+
+    /**
+     * @param {Object} formatters
+     * @param {Object} thisArgument
+     * @param {string|boolean|number|Object} value
+     */
+    partiallyApply: function(formatters, thisArgument, value)
+    {
+        if (value instanceof Array)
+            return value.map(this.partiallyApply.bind(this, formatters, thisArgument));
+        if (typeof value === "object" && typeof formatters[value.type] === "function" && value.arguments)
+            return formatters[value.type].apply(thisArgument, value.arguments);
+        return value;
+    }
+}
+
+WebInspector.auditFormatters = new WebInspector.AuditFormatters();
diff --git a/Source/devtools/front_end/AuditLauncherView.js b/Source/devtools/front_end/AuditLauncherView.js
new file mode 100644
index 0000000..17829b2
--- /dev/null
+++ b/Source/devtools/front_end/AuditLauncherView.js
@@ -0,0 +1,306 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.AuditController} auditController
+ * @extends {WebInspector.View}
+ */
+WebInspector.AuditLauncherView = function(auditController)
+{
+    WebInspector.View.call(this);
+
+    this._auditController = auditController;
+
+    this._categoryIdPrefix = "audit-category-item-";
+    this._auditRunning = false;
+
+    this.element.addStyleClass("audit-launcher-view");
+    this.element.addStyleClass("panel-enabler-view");
+
+    this._contentElement = document.createElement("div");
+    this._contentElement.className = "audit-launcher-view-content";
+    this.element.appendChild(this._contentElement);
+    this._boundCategoryClickListener = this._categoryClicked.bind(this);
+
+    this._resetResourceCount();
+
+    this._sortedCategories = [];
+
+    this._headerElement = document.createElement("h1");
+    this._headerElement.className = "no-audits";
+    this._headerElement.textContent = WebInspector.UIString("No audits to run");
+    this._contentElement.appendChild(this._headerElement);
+
+    WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
+    WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestFinished, this);
+
+    var defaultSelectedAuditCategory = {};
+    defaultSelectedAuditCategory[WebInspector.AuditLauncherView.AllCategoriesKey] = true;
+    this._selectedCategoriesSetting = WebInspector.settings.createSetting("selectedAuditCategories", defaultSelectedAuditCategory);
+}
+
+WebInspector.AuditLauncherView.AllCategoriesKey = "__AllCategories";
+
+WebInspector.AuditLauncherView.prototype = {
+    _resetResourceCount: function()
+    {
+        this._loadedResources = 0;
+        this._totalResources = 0;
+    },
+
+    _onRequestStarted: function(event)
+    {
+        var request = /** @type {WebInspector.NetworkRequest} */ (event.data);
+        // Ignore long-living WebSockets for the sake of progress indicator, as we won't be waiting them anyway.
+        if (request.type === WebInspector.resourceTypes.WebSocket)
+            return;
+        ++this._totalResources;
+        this._updateResourceProgress();
+    },
+
+    _onRequestFinished: function(event)
+    {
+        var request = /** @type {WebInspector.NetworkRequest} */ (event.data);
+        // See resorceStarted for details.
+        if (request.type === WebInspector.resourceTypes.WebSocket)
+            return;
+        ++this._loadedResources;
+        this._updateResourceProgress();
+    },
+
+    /**
+     * @param {!WebInspector.AuditCategory} category
+     */
+    addCategory: function(category)
+    {
+        if (!this._sortedCategories.length)
+            this._createLauncherUI();
+
+        var selectedCategories = this._selectedCategoriesSetting.get();
+        var categoryElement = this._createCategoryElement(category.displayName, category.id);
+        category._checkboxElement = categoryElement.firstChild;
+        if (this._selectAllCheckboxElement.checked || selectedCategories[category.displayName]) {
+            category._checkboxElement.checked = true;
+            ++this._currentCategoriesCount;
+        }
+
+        function compareCategories(a, b)
+        {
+            var aTitle = a.displayName || "";
+            var bTitle = b.displayName || "";
+            return aTitle.localeCompare(bTitle);
+        }
+        var insertBefore = insertionIndexForObjectInListSortedByFunction(category, this._sortedCategories, compareCategories);
+        this._categoriesElement.insertBefore(categoryElement, this._categoriesElement.children[insertBefore]);
+        this._sortedCategories.splice(insertBefore, 0, category);
+        this._selectedCategoriesUpdated();
+    },
+
+    /**
+     * @param {boolean} auditRunning
+     */
+    _setAuditRunning: function(auditRunning)
+    {
+        if (this._auditRunning === auditRunning)
+            return;
+        this._auditRunning = auditRunning;
+        this._updateButton();
+        this._toggleUIComponents(this._auditRunning);
+        if (this._auditRunning)
+            this._startAudit();
+        else
+            this._stopAudit();
+    },
+
+    _startAudit: function()
+    {
+        var catIds = [];
+        for (var category = 0; category < this._sortedCategories.length; ++category) {
+            if (this._sortedCategories[category]._checkboxElement.checked)
+                catIds.push(this._sortedCategories[category].id);
+        }
+
+        this._resetResourceCount();
+        this._progressIndicator = new WebInspector.ProgressIndicator();
+        this._buttonContainerElement.appendChild(this._progressIndicator.element);
+        this._displayResourceLoadingProgress = true;
+
+        function onAuditStarted()
+        {
+            this._displayResourceLoadingProgress = false;
+        }
+        this._auditController.initiateAudit(catIds, this._progressIndicator, this._auditPresentStateElement.checked, onAuditStarted.bind(this), this._setAuditRunning.bind(this, false));
+    },
+
+    _stopAudit: function()
+    {
+        this._displayResourceLoadingProgress = false;
+        this._progressIndicator.cancel();
+        this._progressIndicator.done();
+        delete this._progressIndicator;
+    },
+
+    /**
+     * @param {boolean} disable
+     */
+    _toggleUIComponents: function(disable)
+    {
+        this._selectAllCheckboxElement.disabled = disable;
+        this._categoriesElement.disabled = disable;
+        this._auditPresentStateElement.disabled = disable;
+        this._auditReloadedStateElement.disabled = disable;
+    },
+
+    _launchButtonClicked: function(event)
+    {
+        this._setAuditRunning(!this._auditRunning);
+    },
+
+    /**
+     * @param {boolean} checkCategories
+     * @param {boolean=} userGesture
+     */
+    _selectAllClicked: function(checkCategories, userGesture)
+    {
+        var childNodes = this._categoriesElement.childNodes;
+        for (var i = 0, length = childNodes.length; i < length; ++i)
+            childNodes[i].firstChild.checked = checkCategories;
+        this._currentCategoriesCount = checkCategories ? this._sortedCategories.length : 0;
+        this._selectedCategoriesUpdated(userGesture);
+    },
+
+    _categoryClicked: function(event)
+    {
+        this._currentCategoriesCount += event.target.checked ? 1 : -1;
+        this._selectAllCheckboxElement.checked = this._currentCategoriesCount === this._sortedCategories.length;
+        this._selectedCategoriesUpdated(true);
+    },
+
+    /**
+     * @param {string} title
+     * @param {string} id
+     */
+    _createCategoryElement: function(title, id)
+    {
+        var labelElement = document.createElement("label");
+        labelElement.id = this._categoryIdPrefix + id;
+
+        var element = document.createElement("input");
+        element.type = "checkbox";
+        if (id !== "")
+            element.addEventListener("click", this._boundCategoryClickListener, false);
+        labelElement.appendChild(element);
+        labelElement.appendChild(document.createTextNode(title));
+        labelElement.__displayName = title;
+
+        return labelElement;
+    },
+
+    _createLauncherUI: function()
+    {
+        this._headerElement = document.createElement("h1");
+        this._headerElement.textContent = WebInspector.UIString("Select audits to run");
+
+        for (var child = 0; child < this._contentElement.children.length; ++child)
+            this._contentElement.removeChild(this._contentElement.children[child]);
+
+        this._contentElement.appendChild(this._headerElement);
+
+        function handleSelectAllClick(event)
+        {
+            this._selectAllClicked(event.target.checked, true);
+        }
+        var categoryElement = this._createCategoryElement(WebInspector.UIString("Select All"), "");
+        categoryElement.id = "audit-launcher-selectall";
+        this._selectAllCheckboxElement = categoryElement.firstChild;
+        this._selectAllCheckboxElement.checked = this._selectedCategoriesSetting.get()[WebInspector.AuditLauncherView.AllCategoriesKey];
+        this._selectAllCheckboxElement.addEventListener("click", handleSelectAllClick.bind(this), false);
+        this._contentElement.appendChild(categoryElement);
+
+        this._categoriesElement = this._contentElement.createChild("fieldset", "audit-categories-container");
+        this._currentCategoriesCount = 0;
+
+        this._contentElement.createChild("div", "flexible-space");
+
+        this._buttonContainerElement = this._contentElement.createChild("div", "button-container");
+
+        var labelElement = this._buttonContainerElement.createChild("label");
+        this._auditPresentStateElement = labelElement.createChild("input");
+        this._auditPresentStateElement.name = "audit-mode";
+        this._auditPresentStateElement.type = "radio";
+        this._auditPresentStateElement.checked = true;
+        this._auditPresentStateLabelElement = document.createTextNode(WebInspector.UIString("Audit Present State"));
+        labelElement.appendChild(this._auditPresentStateLabelElement);
+
+        labelElement = this._buttonContainerElement.createChild("label");
+        this._auditReloadedStateElement = labelElement.createChild("input");
+        this._auditReloadedStateElement.name = "audit-mode";
+        this._auditReloadedStateElement.type = "radio";
+        labelElement.appendChild(document.createTextNode("Reload Page and Audit on Load"));
+
+        this._launchButton = this._buttonContainerElement.createChild("button");
+        this._launchButton.textContent = WebInspector.UIString("Run");
+        this._launchButton.addEventListener("click", this._launchButtonClicked.bind(this), false);
+
+        this._selectAllClicked(this._selectAllCheckboxElement.checked);
+    },
+
+    _updateResourceProgress: function()
+    {
+        if (this._displayResourceLoadingProgress)
+            this._progressIndicator.setTitle(WebInspector.UIString("Loading (%d of %d)", this._loadedResources, this._totalResources));
+    },
+
+    /**
+     * @param {boolean=} userGesture
+     */
+    _selectedCategoriesUpdated: function(userGesture)
+    {
+        // Save present categories only upon user gesture to clean up junk from past versions and removed extensions.
+        // Do not remove old categories if not handling a user gesture, as there's chance categories will be added
+        // later during start-up.
+        var selectedCategories = userGesture ? {} : this._selectedCategoriesSetting.get();
+        var childNodes = this._categoriesElement.childNodes;
+        for (var i = 0, length = childNodes.length; i < length; ++i)
+            selectedCategories[childNodes[i].__displayName] = childNodes[i].firstChild.checked;
+        selectedCategories[WebInspector.AuditLauncherView.AllCategoriesKey] = this._selectAllCheckboxElement.checked;
+        this._selectedCategoriesSetting.set(selectedCategories);
+        this._updateButton();
+    },
+
+    _updateButton: function()
+    {
+        this._launchButton.textContent = this._auditRunning ? WebInspector.UIString("Stop") : WebInspector.UIString("Run");
+        this._launchButton.disabled = !this._currentCategoriesCount;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/AuditResultView.js b/Source/devtools/front_end/AuditResultView.js
new file mode 100644
index 0000000..bfeb6f1
--- /dev/null
+++ b/Source/devtools/front_end/AuditResultView.js
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPaneStack}
+ * @param {!Array.<!WebInspector.AuditCategoryResult>} categoryResults
+ */
+WebInspector.AuditResultView = function(categoryResults)
+{
+    WebInspector.SidebarPaneStack.call(this);
+    this.element.addStyleClass("audit-result-view");
+
+    function categorySorter(a, b) {
+        return (a.title || "").localeCompare(b.title || "");
+    }
+    categoryResults.sort(categorySorter);
+    for (var i = 0; i < categoryResults.length; ++i)
+        this.addPane(new WebInspector.AuditCategoryResultPane(categoryResults[i]));
+}
+
+WebInspector.AuditResultView.prototype = {
+    __proto__: WebInspector.SidebarPaneStack.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ * @param {!WebInspector.AuditCategoryResult} categoryResult
+ */
+WebInspector.AuditCategoryResultPane = function(categoryResult)
+{
+    WebInspector.SidebarPane.call(this, categoryResult.title);
+    var treeOutlineElement = document.createElement("ol");
+    this.bodyElement.addStyleClass("audit-result-tree");
+    this.bodyElement.appendChild(treeOutlineElement);
+
+    this._treeOutline = new TreeOutline(treeOutlineElement);
+    this._treeOutline.expandTreeElementsWhenArrowing = true;
+
+    function ruleSorter(a, b)
+    {
+        var result = WebInspector.AuditRule.SeverityOrder[a.severity || 0] - WebInspector.AuditRule.SeverityOrder[b.severity || 0];
+        if (!result)
+            result = (a.value || "").localeCompare(b.value || "");
+        return result;
+    }
+
+    categoryResult.ruleResults.sort(ruleSorter);
+
+    for (var i = 0; i < categoryResult.ruleResults.length; ++i) {
+        var ruleResult = categoryResult.ruleResults[i];
+        var treeElement = this._appendResult(this._treeOutline, ruleResult);
+        treeElement.listItemElement.addStyleClass("audit-result");
+
+        if (ruleResult.severity) {
+            var severityElement = document.createElement("div");
+            severityElement.className = "severity-" + ruleResult.severity;
+            treeElement.listItemElement.appendChild(severityElement);
+        }
+    }
+    this.expand();
+}
+
+WebInspector.AuditCategoryResultPane.prototype = {
+    /**
+     * @param {(TreeOutline|TreeElement)} parentTreeElement
+     * @param {!WebInspector.AuditRuleResult} result
+     */
+    _appendResult: function(parentTreeElement, result)
+    {
+        var title = "";
+
+        if (typeof result.value === "string") {
+            title = result.value;
+            if (result.violationCount)
+                title = String.sprintf("%s (%d)", title, result.violationCount);
+        }
+
+        var treeElement = new TreeElement(null, null, !!result.children);
+        treeElement.title = title;
+        parentTreeElement.appendChild(treeElement);
+
+        if (result.className)
+            treeElement.listItemElement.addStyleClass(result.className);
+        if (typeof result.value !== "string")
+            treeElement.listItemElement.appendChild(WebInspector.auditFormatters.apply(result.value));
+
+        if (result.children) {
+            for (var i = 0; i < result.children.length; ++i)
+                this._appendResult(treeElement, result.children[i]);
+        }
+        if (result.expanded) {
+            treeElement.listItemElement.removeStyleClass("parent");
+            treeElement.listItemElement.addStyleClass("parent-expanded");
+            treeElement.expand();
+        }
+        return treeElement;
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
diff --git a/Source/devtools/front_end/AuditRules.js b/Source/devtools/front_end/AuditRules.js
new file mode 100644
index 0000000..b9c71ff
--- /dev/null
+++ b/Source/devtools/front_end/AuditRules.js
@@ -0,0 +1,1404 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.AuditRules.IPAddressRegexp = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
+
+WebInspector.AuditRules.CacheableResponseCodes =
+{
+    200: true,
+    203: true,
+    206: true,
+    300: true,
+    301: true,
+    410: true,
+
+    304: true // Underlying request is cacheable
+}
+
+/**
+ * @param {!Array.<!WebInspector.NetworkRequest>} requests
+ * @param {Array.<!WebInspector.resourceTypes>} types
+ * @param {boolean} needFullResources
+ * @return {(Object.<string, !Array.<!WebInspector.NetworkRequest>>|Object.<string, !Array.<string>>)}
+ */
+WebInspector.AuditRules.getDomainToResourcesMap = function(requests, types, needFullResources)
+{
+    var domainToResourcesMap = {};
+    for (var i = 0, size = requests.length; i < size; ++i) {
+        var request = requests[i];
+        if (types && types.indexOf(request.type) === -1)
+            continue;
+        var parsedURL = request.url.asParsedURL();
+        if (!parsedURL)
+            continue;
+        var domain = parsedURL.host;
+        var domainResources = domainToResourcesMap[domain];
+        if (domainResources === undefined) {
+          domainResources = [];
+          domainToResourcesMap[domain] = domainResources;
+        }
+        domainResources.push(needFullResources ? request : request.url);
+    }
+    return domainToResourcesMap;
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.GzipRule = function()
+{
+    WebInspector.AuditRule.call(this, "network-gzip", "Enable gzip compression");
+}
+
+WebInspector.AuditRules.GzipRule.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        var totalSavings = 0;
+        var compressedSize = 0;
+        var candidateSize = 0;
+        var summary = result.addChild("", true);
+        for (var i = 0, length = requests.length; i < length; ++i) {
+            var request = requests[i];
+            if (request.statusCode === 304)
+                continue; // Do not test 304 Not Modified requests as their contents are always empty.
+            if (this._shouldCompress(request)) {
+                var size = request.resourceSize;
+                candidateSize += size;
+                if (this._isCompressed(request)) {
+                    compressedSize += size;
+                    continue;
+                }
+                var savings = 2 * size / 3;
+                totalSavings += savings;
+                summary.addFormatted("%r could save ~%s", request.url, Number.bytesToString(savings));
+                result.violationCount++;
+            }
+        }
+        if (!totalSavings)
+            return callback(null);
+        summary.value = String.sprintf("Compressing the following resources with gzip could reduce their transfer size by about two thirds (~%s):", Number.bytesToString(totalSavings));
+        callback(result);
+    },
+
+    _isCompressed: function(request)
+    {
+        var encodingHeader = request.responseHeaderValue("Content-Encoding");
+        if (!encodingHeader)
+            return false;
+
+        return /\b(?:gzip|deflate)\b/.test(encodingHeader);
+    },
+
+    _shouldCompress: function(request)
+    {
+        return request.type.isTextType() && request.parsedURL.host && request.resourceSize !== undefined && request.resourceSize > 150;
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.CombineExternalResourcesRule = function(id, name, type, resourceTypeName, allowedPerDomain)
+{
+    WebInspector.AuditRule.call(this, id, name);
+    this._type = type;
+    this._resourceTypeName = resourceTypeName;
+    this._allowedPerDomain = allowedPerDomain;
+}
+
+WebInspector.AuditRules.CombineExternalResourcesRule.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(requests, [this._type], false);
+        var penalizedResourceCount = 0;
+        // TODO: refactor according to the chosen i18n approach
+        var summary = result.addChild("", true);
+        for (var domain in domainToResourcesMap) {
+            var domainResources = domainToResourcesMap[domain];
+            var extraResourceCount = domainResources.length - this._allowedPerDomain;
+            if (extraResourceCount <= 0)
+                continue;
+            penalizedResourceCount += extraResourceCount - 1;
+            summary.addChild(String.sprintf("%d %s resources served from %s.", domainResources.length, this._resourceTypeName, WebInspector.AuditRuleResult.resourceDomain(domain)));
+            result.violationCount += domainResources.length;
+        }
+        if (!penalizedResourceCount)
+            return callback(null);
+
+        summary.value = "There are multiple resources served from same domain. Consider combining them into as few files as possible.";
+        callback(result);
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRules.CombineExternalResourcesRule}
+ */
+WebInspector.AuditRules.CombineJsResourcesRule = function(allowedPerDomain) {
+    WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externaljs", "Combine external JavaScript", WebInspector.resourceTypes.Script, "JavaScript", allowedPerDomain);
+}
+
+WebInspector.AuditRules.CombineJsResourcesRule.prototype = {
+    __proto__: WebInspector.AuditRules.CombineExternalResourcesRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRules.CombineExternalResourcesRule}
+ */
+WebInspector.AuditRules.CombineCssResourcesRule = function(allowedPerDomain) {
+    WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externalcss", "Combine external CSS", WebInspector.resourceTypes.Stylesheet, "CSS", allowedPerDomain);
+}
+
+WebInspector.AuditRules.CombineCssResourcesRule.prototype = {
+    __proto__: WebInspector.AuditRules.CombineExternalResourcesRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.MinimizeDnsLookupsRule = function(hostCountThreshold) {
+    WebInspector.AuditRule.call(this, "network-minimizelookups", "Minimize DNS lookups");
+    this._hostCountThreshold = hostCountThreshold;
+}
+
+WebInspector.AuditRules.MinimizeDnsLookupsRule.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        var summary = result.addChild("");
+        var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(requests, null, false);
+        for (var domain in domainToResourcesMap) {
+            if (domainToResourcesMap[domain].length > 1)
+                continue;
+            var parsedURL = domain.asParsedURL();
+            if (!parsedURL)
+                continue;
+            if (!parsedURL.host.search(WebInspector.AuditRules.IPAddressRegexp))
+                continue; // an IP address
+            summary.addSnippet(domain);
+            result.violationCount++;
+        }
+        if (!summary.children || summary.children.length <= this._hostCountThreshold)
+            return callback(null);
+
+        summary.value = "The following domains only serve one resource each. If possible, avoid the extra DNS lookups by serving these resources from existing domains.";
+        callback(result);
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.ParallelizeDownloadRule = function(optimalHostnameCount, minRequestThreshold, minBalanceThreshold)
+{
+    WebInspector.AuditRule.call(this, "network-parallelizehosts", "Parallelize downloads across hostnames");
+    this._optimalHostnameCount = optimalHostnameCount;
+    this._minRequestThreshold = minRequestThreshold;
+    this._minBalanceThreshold = minBalanceThreshold;
+}
+
+WebInspector.AuditRules.ParallelizeDownloadRule.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        function hostSorter(a, b)
+        {
+            var aCount = domainToResourcesMap[a].length;
+            var bCount = domainToResourcesMap[b].length;
+            return (aCount < bCount) ? 1 : (aCount == bCount) ? 0 : -1;
+        }
+
+        var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(
+            requests,
+            [WebInspector.resourceTypes.Stylesheet, WebInspector.resourceTypes.Image],
+            true);
+
+        var hosts = [];
+        for (var url in domainToResourcesMap)
+            hosts.push(url);
+
+        if (!hosts.length)
+            return callback(null); // no hosts (local file or something)
+
+        hosts.sort(hostSorter);
+
+        var optimalHostnameCount = this._optimalHostnameCount;
+        if (hosts.length > optimalHostnameCount)
+            hosts.splice(optimalHostnameCount);
+
+        var busiestHostResourceCount = domainToResourcesMap[hosts[0]].length;
+        var requestCountAboveThreshold = busiestHostResourceCount - this._minRequestThreshold;
+        if (requestCountAboveThreshold <= 0)
+            return callback(null);
+
+        var avgResourcesPerHost = 0;
+        for (var i = 0, size = hosts.length; i < size; ++i)
+            avgResourcesPerHost += domainToResourcesMap[hosts[i]].length;
+
+        // Assume optimal parallelization.
+        avgResourcesPerHost /= optimalHostnameCount;
+        avgResourcesPerHost = Math.max(avgResourcesPerHost, 1);
+
+        var pctAboveAvg = (requestCountAboveThreshold / avgResourcesPerHost) - 1.0;
+        var minBalanceThreshold = this._minBalanceThreshold;
+        if (pctAboveAvg < minBalanceThreshold)
+            return callback(null);
+
+        var requestsOnBusiestHost = domainToResourcesMap[hosts[0]];
+        var entry = result.addChild(String.sprintf("This page makes %d parallelizable requests to %s. Increase download parallelization by distributing the following requests across multiple hostnames.", busiestHostResourceCount, hosts[0]), true);
+        for (var i = 0; i < requestsOnBusiestHost.length; ++i)
+            entry.addURL(requestsOnBusiestHost[i].url);
+
+        result.violationCount = requestsOnBusiestHost.length;
+        callback(result);
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * The reported CSS rule size is incorrect (parsed != original in WebKit),
+ * so use percentages instead, which gives a better approximation.
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.UnusedCssRule = function()
+{
+    WebInspector.AuditRule.call(this, "page-unusedcss", "Remove unused CSS rules");
+}
+
+WebInspector.AuditRules.UnusedCssRule.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        var self = this;
+
+        function evalCallback(styleSheets) {
+            if (progress.isCanceled())
+                return;
+
+            if (!styleSheets.length)
+                return callback(null);
+
+            var pseudoSelectorRegexp = /:hover|:link|:active|:visited|:focus|:before|:after/;
+            var selectors = [];
+            var testedSelectors = {};
+            for (var i = 0; i < styleSheets.length; ++i) {
+                var styleSheet = styleSheets[i];
+                for (var curRule = 0; curRule < styleSheet.rules.length; ++curRule) {
+                    var selectorText = styleSheet.rules[curRule].selectorText;
+                    if (selectorText.match(pseudoSelectorRegexp) || testedSelectors[selectorText])
+                        continue;
+                    selectors.push(selectorText);
+                    testedSelectors[selectorText] = 1;
+                }
+            }
+
+            function selectorsCallback(callback, styleSheets, testedSelectors, foundSelectors)
+            {
+                if (progress.isCanceled())
+                    return;
+
+                var inlineBlockOrdinal = 0;
+                var totalStylesheetSize = 0;
+                var totalUnusedStylesheetSize = 0;
+                var summary;
+
+                for (var i = 0; i < styleSheets.length; ++i) {
+                    var styleSheet = styleSheets[i];
+                    var unusedRules = [];
+                    for (var curRule = 0; curRule < styleSheet.rules.length; ++curRule) {
+                        var rule = styleSheet.rules[curRule];
+                        if (!testedSelectors[rule.selectorText] || foundSelectors[rule.selectorText])
+                            continue;
+                        unusedRules.push(rule.selectorText);
+                    }
+                    totalStylesheetSize += styleSheet.rules.length;
+                    totalUnusedStylesheetSize += unusedRules.length;
+
+                    if (!unusedRules.length)
+                        continue;
+
+                    var resource = WebInspector.resourceForURL(styleSheet.sourceURL);
+                    var isInlineBlock = resource && resource.request && resource.request.type == WebInspector.resourceTypes.Document;
+                    var url = !isInlineBlock ? WebInspector.AuditRuleResult.linkifyDisplayName(styleSheet.sourceURL) : String.sprintf("Inline block #%d", ++inlineBlockOrdinal);
+                    var pctUnused = Math.round(100 * unusedRules.length / styleSheet.rules.length);
+                    if (!summary)
+                        summary = result.addChild("", true);
+                    var entry = summary.addFormatted("%s: %d% is not used by the current page.", url, pctUnused);
+
+                    for (var j = 0; j < unusedRules.length; ++j)
+                        entry.addSnippet(unusedRules[j]);
+
+                    result.violationCount += unusedRules.length;
+                }
+
+                if (!totalUnusedStylesheetSize)
+                    return callback(null);
+
+                var totalUnusedPercent = Math.round(100 * totalUnusedStylesheetSize / totalStylesheetSize);
+                summary.value = String.sprintf("%s rules (%d%) of CSS not used by the current page.", totalUnusedStylesheetSize, totalUnusedPercent);
+
+                callback(result);
+            }
+
+            var foundSelectors = {};
+            function queryCallback(boundSelectorsCallback, selector, styleSheets, testedSelectors, nodeId)
+            {
+                if (nodeId)
+                    foundSelectors[selector] = true;
+                if (boundSelectorsCallback)
+                    boundSelectorsCallback(foundSelectors);
+            }
+
+            function documentLoaded(selectors, document) {
+                for (var i = 0; i < selectors.length; ++i) {
+                    if (progress.isCanceled())
+                        return;
+                    WebInspector.domAgent.querySelector(document.id, selectors[i], queryCallback.bind(null, i === selectors.length - 1 ? selectorsCallback.bind(null, callback, styleSheets, testedSelectors) : null, selectors[i], styleSheets, testedSelectors));
+                }
+            }
+
+            WebInspector.domAgent.requestDocument(documentLoaded.bind(null, selectors));
+        }
+
+        function styleSheetCallback(styleSheets, sourceURL, continuation, styleSheet)
+        {
+            if (progress.isCanceled())
+                return;
+
+            if (styleSheet) {
+                styleSheet.sourceURL = sourceURL;
+                styleSheets.push(styleSheet);
+            }
+            if (continuation)
+                continuation(styleSheets);
+        }
+
+        function allStylesCallback(error, styleSheetInfos)
+        {
+            if (progress.isCanceled())
+                return;
+
+            if (error || !styleSheetInfos || !styleSheetInfos.length)
+                return evalCallback([]);
+            var styleSheets = [];
+            for (var i = 0; i < styleSheetInfos.length; ++i) {
+                var info = styleSheetInfos[i];
+                WebInspector.CSSStyleSheet.createForId(info.styleSheetId, styleSheetCallback.bind(null, styleSheets, info.sourceURL, i == styleSheetInfos.length - 1 ? evalCallback : null));
+            }
+        }
+
+        CSSAgent.getAllStyleSheets(allStylesCallback);
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.CacheControlRule = function(id, name)
+{
+    WebInspector.AuditRule.call(this, id, name);
+}
+
+WebInspector.AuditRules.CacheControlRule.MillisPerMonth = 1000 * 60 * 60 * 24 * 30;
+
+WebInspector.AuditRules.CacheControlRule.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        var cacheableAndNonCacheableResources = this._cacheableAndNonCacheableResources(requests);
+        if (cacheableAndNonCacheableResources[0].length)
+            this.runChecks(cacheableAndNonCacheableResources[0], result);
+        this.handleNonCacheableResources(cacheableAndNonCacheableResources[1], result);
+
+        callback(result);
+    },
+
+    handleNonCacheableResources: function(requests, result)
+    {
+    },
+
+    _cacheableAndNonCacheableResources: function(requests)
+    {
+        var processedResources = [[], []];
+        for (var i = 0; i < requests.length; ++i) {
+            var request = requests[i];
+            if (!this.isCacheableResource(request))
+                continue;
+            if (this._isExplicitlyNonCacheable(request))
+                processedResources[1].push(request);
+            else
+                processedResources[0].push(request);
+        }
+        return processedResources;
+    },
+
+    execCheck: function(messageText, requestCheckFunction, requests, result)
+    {
+        var requestCount = requests.length;
+        var urls = [];
+        for (var i = 0; i < requestCount; ++i) {
+            if (requestCheckFunction.call(this, requests[i]))
+                urls.push(requests[i].url);
+        }
+        if (urls.length) {
+            var entry = result.addChild(messageText, true);
+            entry.addURLs(urls);
+            result.violationCount += urls.length;
+        }
+    },
+
+    freshnessLifetimeGreaterThan: function(request, timeMs)
+    {
+        var dateHeader = this.responseHeader(request, "Date");
+        if (!dateHeader)
+            return false;
+
+        var dateHeaderMs = Date.parse(dateHeader);
+        if (isNaN(dateHeaderMs))
+            return false;
+
+        var freshnessLifetimeMs;
+        var maxAgeMatch = this.responseHeaderMatch(request, "Cache-Control", "max-age=(\\d+)");
+
+        if (maxAgeMatch)
+            freshnessLifetimeMs = (maxAgeMatch[1]) ? 1000 * maxAgeMatch[1] : 0;
+        else {
+            var expiresHeader = this.responseHeader(request, "Expires");
+            if (expiresHeader) {
+                var expDate = Date.parse(expiresHeader);
+                if (!isNaN(expDate))
+                    freshnessLifetimeMs = expDate - dateHeaderMs;
+            }
+        }
+
+        return (isNaN(freshnessLifetimeMs)) ? false : freshnessLifetimeMs > timeMs;
+    },
+
+    responseHeader: function(request, header)
+    {
+        return request.responseHeaderValue(header);
+    },
+
+    hasResponseHeader: function(request, header)
+    {
+        return request.responseHeaderValue(header) !== undefined;
+    },
+
+    isCompressible: function(request)
+    {
+        return request.type.isTextType();
+    },
+
+    isPubliclyCacheable: function(request)
+    {
+        if (this._isExplicitlyNonCacheable(request))
+            return false;
+
+        if (this.responseHeaderMatch(request, "Cache-Control", "public"))
+            return true;
+
+        return request.url.indexOf("?") == -1 && !this.responseHeaderMatch(request, "Cache-Control", "private");
+    },
+
+    responseHeaderMatch: function(request, header, regexp)
+    {
+        return request.responseHeaderValue(header)
+            ? request.responseHeaderValue(header).match(new RegExp(regexp, "im"))
+            : undefined;
+    },
+
+    hasExplicitExpiration: function(request)
+    {
+        return this.hasResponseHeader(request, "Date") &&
+            (this.hasResponseHeader(request, "Expires") || this.responseHeaderMatch(request, "Cache-Control", "max-age"));
+    },
+
+    _isExplicitlyNonCacheable: function(request)
+    {
+        var hasExplicitExp = this.hasExplicitExpiration(request);
+        return this.responseHeaderMatch(request, "Cache-Control", "(no-cache|no-store|must-revalidate)") ||
+            this.responseHeaderMatch(request, "Pragma", "no-cache") ||
+            (hasExplicitExp && !this.freshnessLifetimeGreaterThan(request, 0)) ||
+            (!hasExplicitExp && request.url && request.url.indexOf("?") >= 0) ||
+            (!hasExplicitExp && !this.isCacheableResource(request));
+    },
+
+    isCacheableResource: function(request)
+    {
+        return request.statusCode !== undefined && WebInspector.AuditRules.CacheableResponseCodes[request.statusCode];
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRules.CacheControlRule}
+ */
+WebInspector.AuditRules.BrowserCacheControlRule = function()
+{
+    WebInspector.AuditRules.CacheControlRule.call(this, "http-browsercache", "Leverage browser caching");
+}
+
+WebInspector.AuditRules.BrowserCacheControlRule.prototype = {
+    handleNonCacheableResources: function(requests, result)
+    {
+        if (requests.length) {
+            var entry = result.addChild("The following resources are explicitly non-cacheable. Consider making them cacheable if possible:", true);
+            result.violationCount += requests.length;
+            for (var i = 0; i < requests.length; ++i)
+                entry.addURL(requests[i].url);
+        }
+    },
+
+    runChecks: function(requests, result, callback)
+    {
+        this.execCheck("The following resources are missing a cache expiration. Resources that do not specify an expiration may not be cached by browsers:",
+            this._missingExpirationCheck, requests, result);
+        this.execCheck("The following resources specify a \"Vary\" header that disables caching in most versions of Internet Explorer:",
+            this._varyCheck, requests, result);
+        this.execCheck("The following cacheable resources have a short freshness lifetime:",
+            this._oneMonthExpirationCheck, requests, result);
+
+        // Unable to implement the favicon check due to the WebKit limitations.
+        this.execCheck("To further improve cache hit rate, specify an expiration one year in the future for the following cacheable resources:",
+            this._oneYearExpirationCheck, requests, result);
+    },
+
+    _missingExpirationCheck: function(request)
+    {
+        return this.isCacheableResource(request) && !this.hasResponseHeader(request, "Set-Cookie") && !this.hasExplicitExpiration(request);
+    },
+
+    _varyCheck: function(request)
+    {
+        var varyHeader = this.responseHeader(request, "Vary");
+        if (varyHeader) {
+            varyHeader = varyHeader.replace(/User-Agent/gi, "");
+            varyHeader = varyHeader.replace(/Accept-Encoding/gi, "");
+            varyHeader = varyHeader.replace(/[, ]*/g, "");
+        }
+        return varyHeader && varyHeader.length && this.isCacheableResource(request) && this.freshnessLifetimeGreaterThan(request, 0);
+    },
+
+    _oneMonthExpirationCheck: function(request)
+    {
+        return this.isCacheableResource(request) &&
+            !this.hasResponseHeader(request, "Set-Cookie") &&
+            !this.freshnessLifetimeGreaterThan(request, WebInspector.AuditRules.CacheControlRule.MillisPerMonth) &&
+            this.freshnessLifetimeGreaterThan(request, 0);
+    },
+
+    _oneYearExpirationCheck: function(request)
+    {
+        return this.isCacheableResource(request) &&
+            !this.hasResponseHeader(request, "Set-Cookie") &&
+            !this.freshnessLifetimeGreaterThan(request, 11 * WebInspector.AuditRules.CacheControlRule.MillisPerMonth) &&
+            this.freshnessLifetimeGreaterThan(request, WebInspector.AuditRules.CacheControlRule.MillisPerMonth);
+    },
+
+    __proto__: WebInspector.AuditRules.CacheControlRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRules.CacheControlRule}
+ */
+WebInspector.AuditRules.ProxyCacheControlRule = function() {
+    WebInspector.AuditRules.CacheControlRule.call(this, "http-proxycache", "Leverage proxy caching");
+}
+
+WebInspector.AuditRules.ProxyCacheControlRule.prototype = {
+    runChecks: function(requests, result, callback)
+    {
+        this.execCheck("Resources with a \"?\" in the URL are not cached by most proxy caching servers:",
+            this._questionMarkCheck, requests, result);
+        this.execCheck("Consider adding a \"Cache-Control: public\" header to the following resources:",
+            this._publicCachingCheck, requests, result);
+        this.execCheck("The following publicly cacheable resources contain a Set-Cookie header. This security vulnerability can cause cookies to be shared by multiple users.",
+            this._setCookieCacheableCheck, requests, result);
+    },
+
+    _questionMarkCheck: function(request)
+    {
+        return request.url.indexOf("?") >= 0 && !this.hasResponseHeader(request, "Set-Cookie") && this.isPubliclyCacheable(request);
+    },
+
+    _publicCachingCheck: function(request)
+    {
+        return this.isCacheableResource(request) &&
+            !this.isCompressible(request) &&
+            !this.responseHeaderMatch(request, "Cache-Control", "public") &&
+            !this.hasResponseHeader(request, "Set-Cookie");
+    },
+
+    _setCookieCacheableCheck: function(request)
+    {
+        return this.hasResponseHeader(request, "Set-Cookie") && this.isPubliclyCacheable(request);
+    },
+
+    __proto__: WebInspector.AuditRules.CacheControlRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.ImageDimensionsRule = function()
+{
+    WebInspector.AuditRule.call(this, "page-imagedims", "Specify image dimensions");
+}
+
+WebInspector.AuditRules.ImageDimensionsRule.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        var urlToNoDimensionCount = {};
+
+        function doneCallback()
+        {
+            for (var url in urlToNoDimensionCount) {
+                var entry = entry || result.addChild("A width and height should be specified for all images in order to speed up page display. The following image(s) are missing a width and/or height:", true);
+                var format = "%r";
+                if (urlToNoDimensionCount[url] > 1)
+                    format += " (%d uses)";
+                entry.addFormatted(format, url, urlToNoDimensionCount[url]);
+                result.violationCount++;
+            }
+            callback(entry ? result : null);
+        }
+
+        function imageStylesReady(imageId, styles, isLastStyle, computedStyle)
+        {
+            if (progress.isCanceled())
+                return;
+
+            const node = WebInspector.domAgent.nodeForId(imageId);
+            var src = node.getAttribute("src");
+            if (!src.asParsedURL()) {
+                for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
+                    if (frameOwnerCandidate.baseURL) {
+                        var completeSrc = WebInspector.ParsedURL.completeURL(frameOwnerCandidate.baseURL, src);
+                        break;
+                    }
+                }
+            }
+            if (completeSrc)
+                src = completeSrc;
+
+            if (computedStyle.getPropertyValue("position") === "absolute") {
+                if (isLastStyle)
+                    doneCallback();
+                return;
+            }
+
+            if (styles.attributesStyle) {
+                var widthFound = !!styles.attributesStyle.getLiveProperty("width");
+                var heightFound = !!styles.attributesStyle.getLiveProperty("height");
+            }
+
+            var inlineStyle = styles.inlineStyle;
+            if (inlineStyle) {
+                if (inlineStyle.getPropertyValue("width") !== "")
+                    widthFound = true;
+                if (inlineStyle.getPropertyValue("height") !== "")
+                    heightFound = true;
+            }
+
+            for (var i = styles.matchedCSSRules.length - 1; i >= 0 && !(widthFound && heightFound); --i) {
+                var style = styles.matchedCSSRules[i].style;
+                if (style.getPropertyValue("width") !== "")
+                    widthFound = true;
+                if (style.getPropertyValue("height") !== "")
+                    heightFound = true;
+            }
+
+            if (!widthFound || !heightFound) {
+                if (src in urlToNoDimensionCount)
+                    ++urlToNoDimensionCount[src];
+                else
+                    urlToNoDimensionCount[src] = 1;
+            }
+
+            if (isLastStyle)
+                doneCallback();
+        }
+
+        function getStyles(nodeIds)
+        {
+            if (progress.isCanceled())
+                return;
+            var targetResult = {};
+
+            function inlineCallback(inlineStyle, attributesStyle)
+            {
+                targetResult.inlineStyle = inlineStyle;
+                targetResult.attributesStyle = attributesStyle;
+            }
+
+            function matchedCallback(result)
+            {
+                if (result)
+                    targetResult.matchedCSSRules = result.matchedCSSRules;
+            }
+
+            if (!nodeIds || !nodeIds.length)
+                doneCallback();
+
+            for (var i = 0; nodeIds && i < nodeIds.length; ++i) {
+                WebInspector.cssModel.getMatchedStylesAsync(nodeIds[i], false, false, matchedCallback);
+                WebInspector.cssModel.getInlineStylesAsync(nodeIds[i], inlineCallback);
+                WebInspector.cssModel.getComputedStyleAsync(nodeIds[i], imageStylesReady.bind(null, nodeIds[i], targetResult, i === nodeIds.length - 1));
+            }
+        }
+
+        function onDocumentAvailable(root)
+        {
+            if (progress.isCanceled())
+                return;
+            WebInspector.domAgent.querySelectorAll(root.id, "img[src]", getStyles);
+        }
+
+        if (progress.isCanceled())
+            return;
+        WebInspector.domAgent.requestDocument(onDocumentAvailable);
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.CssInHeadRule = function()
+{
+    WebInspector.AuditRule.call(this, "page-cssinhead", "Put CSS in the document head");
+}
+
+WebInspector.AuditRules.CssInHeadRule.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        function evalCallback(evalResult)
+        {
+            if (progress.isCanceled())
+                return;
+
+            if (!evalResult)
+                return callback(null);
+
+            var summary = result.addChild("");
+
+            var outputMessages = [];
+            for (var url in evalResult) {
+                var urlViolations = evalResult[url];
+                if (urlViolations[0]) {
+                    result.addFormatted("%s style block(s) in the %r body should be moved to the document head.", urlViolations[0], url);
+                    result.violationCount += urlViolations[0];
+                }
+                for (var i = 0; i < urlViolations[1].length; ++i)
+                    result.addFormatted("Link node %r should be moved to the document head in %r", urlViolations[1][i], url);
+                result.violationCount += urlViolations[1].length;
+            }
+            summary.value = String.sprintf("CSS in the document body adversely impacts rendering performance.");
+            callback(result);
+        }
+
+        function externalStylesheetsReceived(root, inlineStyleNodeIds, nodeIds)
+        {
+            if (progress.isCanceled())
+                return;
+
+            if (!nodeIds)
+                return;
+            var externalStylesheetNodeIds = nodeIds;
+            var result = null;
+            if (inlineStyleNodeIds.length || externalStylesheetNodeIds.length) {
+                var urlToViolationsArray = {};
+                var externalStylesheetHrefs = [];
+                for (var j = 0; j < externalStylesheetNodeIds.length; ++j) {
+                    var linkNode = WebInspector.domAgent.nodeForId(externalStylesheetNodeIds[j]);
+                    var completeHref = WebInspector.ParsedURL.completeURL(linkNode.ownerDocument.baseURL, linkNode.getAttribute("href"));
+                    externalStylesheetHrefs.push(completeHref || "<empty>");
+                }
+                urlToViolationsArray[root.documentURL] = [inlineStyleNodeIds.length, externalStylesheetHrefs];
+                result = urlToViolationsArray;
+            }
+            evalCallback(result);
+        }
+
+        function inlineStylesReceived(root, nodeIds)
+        {
+            if (progress.isCanceled())
+                return;
+
+            if (!nodeIds)
+                return;
+            WebInspector.domAgent.querySelectorAll(root.id, "body link[rel~='stylesheet'][href]", externalStylesheetsReceived.bind(null, root, nodeIds));
+        }
+
+        function onDocumentAvailable(root)
+        {
+            if (progress.isCanceled())
+                return;
+
+            WebInspector.domAgent.querySelectorAll(root.id, "body style", inlineStylesReceived.bind(null, root));
+        }
+
+        WebInspector.domAgent.requestDocument(onDocumentAvailable);
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.StylesScriptsOrderRule = function()
+{
+    WebInspector.AuditRule.call(this, "page-stylescriptorder", "Optimize the order of styles and scripts");
+}
+
+WebInspector.AuditRules.StylesScriptsOrderRule.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        function evalCallback(resultValue)
+        {
+            if (progress.isCanceled())
+                return;
+
+            if (!resultValue)
+                return callback(null);
+
+            var lateCssUrls = resultValue[0];
+            var cssBeforeInlineCount = resultValue[1];
+
+            var entry = result.addChild("The following external CSS files were included after an external JavaScript file in the document head. To ensure CSS files are downloaded in parallel, always include external CSS before external JavaScript.", true);
+            entry.addURLs(lateCssUrls);
+            result.violationCount += lateCssUrls.length;
+
+            if (cssBeforeInlineCount) {
+                result.addChild(String.sprintf(" %d inline script block%s found in the head between an external CSS file and another resource. To allow parallel downloading, move the inline script before the external CSS file, or after the next resource.", cssBeforeInlineCount, cssBeforeInlineCount > 1 ? "s were" : " was"));
+                result.violationCount += cssBeforeInlineCount;
+            }
+            callback(result);
+        }
+
+        function cssBeforeInlineReceived(lateStyleIds, nodeIds)
+        {
+            if (progress.isCanceled())
+                return;
+
+            if (!nodeIds)
+                return;
+
+            var cssBeforeInlineCount = nodeIds.length;
+            var result = null;
+            if (lateStyleIds.length || cssBeforeInlineCount) {
+                var lateStyleUrls = [];
+                for (var i = 0; i < lateStyleIds.length; ++i) {
+                    var lateStyleNode = WebInspector.domAgent.nodeForId(lateStyleIds[i]);
+                    var completeHref = WebInspector.ParsedURL.completeURL(lateStyleNode.ownerDocument.baseURL, lateStyleNode.getAttribute("href"));
+                    lateStyleUrls.push(completeHref || "<empty>");
+                }
+                result = [ lateStyleUrls, cssBeforeInlineCount ];
+            }
+
+            evalCallback(result);
+        }
+
+        function lateStylesReceived(root, nodeIds)
+        {
+            if (progress.isCanceled())
+                return;
+
+            if (!nodeIds)
+                return;
+
+            WebInspector.domAgent.querySelectorAll(root.id, "head link[rel~='stylesheet'][href] ~ script:not([src])", cssBeforeInlineReceived.bind(null, nodeIds));
+        }
+
+        function onDocumentAvailable(root)
+        {
+            if (progress.isCanceled())
+                return;
+
+            WebInspector.domAgent.querySelectorAll(root.id, "head script[src] ~ link[rel~='stylesheet'][href]", lateStylesReceived.bind(null, root));
+        }
+
+        WebInspector.domAgent.requestDocument(onDocumentAvailable);
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.CSSRuleBase = function(id, name)
+{
+    WebInspector.AuditRule.call(this, id, name);
+}
+
+WebInspector.AuditRules.CSSRuleBase.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        CSSAgent.getAllStyleSheets(sheetsCallback.bind(this));
+
+        function sheetsCallback(error, headers)
+        {
+            if (error)
+                return callback(null);
+
+            if (!headers.length)
+                return callback(null);
+            for (var i = 0; i < headers.length; ++i) {
+                var header = headers[i];
+                if (header.disabled)
+                    continue; // Do not check disabled stylesheets.
+
+                this._visitStyleSheet(header.styleSheetId, i === headers.length - 1 ? finishedCallback : null, result, progress);
+            }
+        }
+
+        function finishedCallback()
+        {
+            callback(result);
+        }
+    },
+
+    _visitStyleSheet: function(styleSheetId, callback, result, progress)
+    {
+        WebInspector.CSSStyleSheet.createForId(styleSheetId, sheetCallback.bind(this));
+
+        function sheetCallback(styleSheet)
+        {
+            if (progress.isCanceled())
+                return;
+
+            if (!styleSheet) {
+                if (callback)
+                    callback();
+                return;
+            }
+
+            this.visitStyleSheet(styleSheet, result);
+
+            for (var i = 0; i < styleSheet.rules.length; ++i)
+                this._visitRule(styleSheet, styleSheet.rules[i], result);
+
+            this.didVisitStyleSheet(styleSheet, result);
+
+            if (callback)
+                callback();
+        }
+    },
+
+    _visitRule: function(styleSheet, rule, result)
+    {
+        this.visitRule(styleSheet, rule, result);
+        var allProperties = rule.style.allProperties;
+        for (var i = 0; i < allProperties.length; ++i)
+            this.visitProperty(styleSheet, allProperties[i], result);
+        this.didVisitRule(styleSheet, rule, result);
+    },
+
+    visitStyleSheet: function(styleSheet, result)
+    {
+        // Subclasses can implement.
+    },
+
+    didVisitStyleSheet: function(styleSheet, result)
+    {
+        // Subclasses can implement.
+    },
+    
+    visitRule: function(styleSheet, rule, result)
+    {
+        // Subclasses can implement.
+    },
+
+    didVisitRule: function(styleSheet, rule, result)
+    {
+        // Subclasses can implement.
+    },
+    
+    visitProperty: function(styleSheet, property, result)
+    {
+        // Subclasses can implement.
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRules.CSSRuleBase}
+ */
+WebInspector.AuditRules.VendorPrefixedCSSProperties = function()
+{
+    WebInspector.AuditRules.CSSRuleBase.call(this, "page-vendorprefixedcss", "Use normal CSS property names instead of vendor-prefixed ones");
+    this._webkitPrefix = "-webkit-";
+}
+
+WebInspector.AuditRules.VendorPrefixedCSSProperties.supportedProperties = [
+    "background-clip", "background-origin", "background-size",
+    "border-radius", "border-bottom-left-radius", "border-bottom-right-radius", "border-top-left-radius", "border-top-right-radius",
+    "box-shadow", "box-sizing", "opacity", "text-shadow"
+].keySet();
+
+WebInspector.AuditRules.VendorPrefixedCSSProperties.prototype = {
+    didVisitStyleSheet: function(styleSheet)
+    {
+        delete this._styleSheetResult;
+    },
+
+    visitRule: function(rule)
+    {
+        this._mentionedProperties = {};
+    },
+
+    didVisitRule: function()
+    {
+        delete this._ruleResult;
+        delete this._mentionedProperties;
+    },
+
+    visitProperty: function(styleSheet, property, result)
+    {
+        if (!property.name.startsWith(this._webkitPrefix))
+            return;
+
+        var normalPropertyName = property.name.substring(this._webkitPrefix.length).toLowerCase(); // Start just after the "-webkit-" prefix.
+        if (WebInspector.AuditRules.VendorPrefixedCSSProperties.supportedProperties[normalPropertyName] && !this._mentionedProperties[normalPropertyName]) {
+            var style = property.ownerStyle;
+            var liveProperty = style.getLiveProperty(normalPropertyName);
+            if (liveProperty && !liveProperty.styleBased)
+                return; // WebCore can provide normal versions of prefixed properties automatically, so be careful to skip only normal source-based properties.
+
+            var rule = style.parentRule;
+            this._mentionedProperties[normalPropertyName] = true;
+            if (!this._styleSheetResult)
+                this._styleSheetResult = result.addChild(rule.sourceURL ? WebInspector.linkifyResourceAsNode(rule.sourceURL) : "<unknown>");
+            if (!this._ruleResult) {
+                var anchor = WebInspector.linkifyURLAsNode(rule.sourceURL, rule.selectorText);
+                anchor.preferredPanel = "resources";
+                anchor.lineNumber = rule.sourceLine;
+                this._ruleResult = this._styleSheetResult.addChild(anchor);
+            }
+            ++result.violationCount;
+            this._ruleResult.addSnippet(String.sprintf("\"" + this._webkitPrefix + "%s\" is used, but \"%s\" is supported.", normalPropertyName, normalPropertyName));
+        }
+    },
+
+    __proto__: WebInspector.AuditRules.CSSRuleBase.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRule}
+ */
+WebInspector.AuditRules.CookieRuleBase = function(id, name)
+{
+    WebInspector.AuditRule.call(this, id, name);
+}
+
+WebInspector.AuditRules.CookieRuleBase.prototype = {
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {!WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        var self = this;
+        function resultCallback(receivedCookies) {
+            if (progress.isCanceled())
+                return;
+
+            self.processCookies(receivedCookies, requests, result);
+            callback(result);
+        }
+
+        WebInspector.Cookies.getCookiesAsync(resultCallback);
+    },
+
+    mapResourceCookies: function(requestsByDomain, allCookies, callback)
+    {
+        for (var i = 0; i < allCookies.length; ++i) {
+            for (var requestDomain in requestsByDomain) {
+                if (WebInspector.Cookies.cookieDomainMatchesResourceDomain(allCookies[i].domain(), requestDomain))
+                    this._callbackForResourceCookiePairs(requestsByDomain[requestDomain], allCookies[i], callback);
+            }
+        }
+    },
+
+    _callbackForResourceCookiePairs: function(requests, cookie, callback)
+    {
+        if (!requests)
+            return;
+        for (var i = 0; i < requests.length; ++i) {
+            if (WebInspector.Cookies.cookieMatchesResourceURL(cookie, requests[i].url))
+                callback(requests[i], cookie);
+        }
+    },
+
+    __proto__: WebInspector.AuditRule.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRules.CookieRuleBase}
+ */
+WebInspector.AuditRules.CookieSizeRule = function(avgBytesThreshold)
+{
+    WebInspector.AuditRules.CookieRuleBase.call(this, "http-cookiesize", "Minimize cookie size");
+    this._avgBytesThreshold = avgBytesThreshold;
+    this._maxBytesThreshold = 1000;
+}
+
+WebInspector.AuditRules.CookieSizeRule.prototype = {
+    _average: function(cookieArray)
+    {
+        var total = 0;
+        for (var i = 0; i < cookieArray.length; ++i)
+            total += cookieArray[i].size();
+        return cookieArray.length ? Math.round(total / cookieArray.length) : 0;
+    },
+
+    _max: function(cookieArray)
+    {
+        var result = 0;
+        for (var i = 0; i < cookieArray.length; ++i)
+            result = Math.max(cookieArray[i].size(), result);
+        return result;
+    },
+
+    processCookies: function(allCookies, requests, result)
+    {
+        function maxSizeSorter(a, b)
+        {
+            return b.maxCookieSize - a.maxCookieSize;
+        }
+
+        function avgSizeSorter(a, b)
+        {
+            return b.avgCookieSize - a.avgCookieSize;
+        }
+
+        var cookiesPerResourceDomain = {};
+
+        function collectorCallback(request, cookie)
+        {
+            var cookies = cookiesPerResourceDomain[request.parsedURL.host];
+            if (!cookies) {
+                cookies = [];
+                cookiesPerResourceDomain[request.parsedURL.host] = cookies;
+            }
+            cookies.push(cookie);
+        }
+
+        if (!allCookies.length)
+            return;
+
+        var sortedCookieSizes = [];
+
+        var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(requests,
+                null,
+                true);
+        var matchingResourceData = {};
+        this.mapResourceCookies(domainToResourcesMap, allCookies, collectorCallback.bind(this));
+
+        for (var requestDomain in cookiesPerResourceDomain) {
+            var cookies = cookiesPerResourceDomain[requestDomain];
+            sortedCookieSizes.push({
+                domain: requestDomain,
+                avgCookieSize: this._average(cookies),
+                maxCookieSize: this._max(cookies)
+            });
+        }
+        var avgAllCookiesSize = this._average(allCookies);
+
+        var hugeCookieDomains = [];
+        sortedCookieSizes.sort(maxSizeSorter);
+
+        for (var i = 0, len = sortedCookieSizes.length; i < len; ++i) {
+            var maxCookieSize = sortedCookieSizes[i].maxCookieSize;
+            if (maxCookieSize > this._maxBytesThreshold)
+                hugeCookieDomains.push(WebInspector.AuditRuleResult.resourceDomain(sortedCookieSizes[i].domain) + ": " + Number.bytesToString(maxCookieSize));
+        }
+
+        var bigAvgCookieDomains = [];
+        sortedCookieSizes.sort(avgSizeSorter);
+        for (var i = 0, len = sortedCookieSizes.length; i < len; ++i) {
+            var domain = sortedCookieSizes[i].domain;
+            var avgCookieSize = sortedCookieSizes[i].avgCookieSize;
+            if (avgCookieSize > this._avgBytesThreshold && avgCookieSize < this._maxBytesThreshold)
+                bigAvgCookieDomains.push(WebInspector.AuditRuleResult.resourceDomain(domain) + ": " + Number.bytesToString(avgCookieSize));
+        }
+        result.addChild(String.sprintf("The average cookie size for all requests on this page is %s", Number.bytesToString(avgAllCookiesSize)));
+
+        var message;
+        if (hugeCookieDomains.length) {
+            var entry = result.addChild("The following domains have a cookie size in excess of 1KB. This is harmful because requests with cookies larger than 1KB typically cannot fit into a single network packet.", true);
+            entry.addURLs(hugeCookieDomains);
+            result.violationCount += hugeCookieDomains.length;
+        }
+
+        if (bigAvgCookieDomains.length) {
+            var entry = result.addChild(String.sprintf("The following domains have an average cookie size in excess of %d bytes. Reducing the size of cookies for these domains can reduce the time it takes to send requests.", this._avgBytesThreshold), true);
+            entry.addURLs(bigAvgCookieDomains);
+            result.violationCount += bigAvgCookieDomains.length;
+        }
+    },
+
+    __proto__: WebInspector.AuditRules.CookieRuleBase.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditRules.CookieRuleBase}
+ */
+WebInspector.AuditRules.StaticCookielessRule = function(minResources)
+{
+    WebInspector.AuditRules.CookieRuleBase.call(this, "http-staticcookieless", "Serve static content from a cookieless domain");
+    this._minResources = minResources;
+}
+
+WebInspector.AuditRules.StaticCookielessRule.prototype = {
+    processCookies: function(allCookies, requests, result)
+    {
+        var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(requests,
+                [WebInspector.resourceTypes.Stylesheet,
+                 WebInspector.resourceTypes.Image],
+                true);
+        var totalStaticResources = 0;
+        for (var domain in domainToResourcesMap)
+            totalStaticResources += domainToResourcesMap[domain].length;
+        if (totalStaticResources < this._minResources)
+            return;
+        var matchingResourceData = {};
+        this.mapResourceCookies(domainToResourcesMap, allCookies, this._collectorCallback.bind(this, matchingResourceData));
+
+        var badUrls = [];
+        var cookieBytes = 0;
+        for (var url in matchingResourceData) {
+            badUrls.push(url);
+            cookieBytes += matchingResourceData[url]
+        }
+        if (badUrls.length < this._minResources)
+            return;
+
+        var entry = result.addChild(String.sprintf("%s of cookies were sent with the following static resources. Serve these static resources from a domain that does not set cookies:", Number.bytesToString(cookieBytes)), true);
+        entry.addURLs(badUrls);
+        result.violationCount = badUrls.length;
+    },
+
+    _collectorCallback: function(matchingResourceData, request, cookie)
+    {
+        matchingResourceData[request.url] = (matchingResourceData[request.url] || 0) + cookie.size();
+    },
+
+    __proto__: WebInspector.AuditRules.CookieRuleBase.prototype
+}
diff --git a/Source/devtools/front_end/AuditsPanel.js b/Source/devtools/front_end/AuditsPanel.js
new file mode 100644
index 0000000..c347500
--- /dev/null
+++ b/Source/devtools/front_end/AuditsPanel.js
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Panel}
+ */
+WebInspector.AuditsPanel = function()
+{
+    WebInspector.Panel.call(this, "audits");
+    this.registerRequiredCSS("panelEnablerView.css");
+    this.registerRequiredCSS("auditsPanel.css");
+
+    this.createSidebarViewWithTree();
+    this.auditsTreeElement = new WebInspector.SidebarSectionTreeElement("", {}, true);
+    this.sidebarTree.appendChild(this.auditsTreeElement);
+    this.auditsTreeElement.listItemElement.addStyleClass("hidden");
+
+    this.auditsItemTreeElement = new WebInspector.AuditsSidebarTreeElement(this);
+    this.auditsTreeElement.appendChild(this.auditsItemTreeElement);
+
+    this.auditResultsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RESULTS"), {}, true);
+    this.sidebarTree.appendChild(this.auditResultsTreeElement);
+    this.auditResultsTreeElement.expand();
+
+    this.clearResultsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear audit results."), "clear-status-bar-item");
+    this.clearResultsButton.addEventListener("click", this._clearButtonClicked, this);
+
+    this.viewsContainerElement = this.splitView.mainElement;
+
+    this._constructCategories();
+
+    this._auditController = new WebInspector.AuditController(this);
+    this._launcherView = new WebInspector.AuditLauncherView(this._auditController);
+    for (var id in this.categoriesById)
+        this._launcherView.addCategory(this.categoriesById[id]);
+}
+
+WebInspector.AuditsPanel.prototype = {
+    get statusBarItems()
+    {
+        return [this.clearResultsButton.element];
+    },
+
+    /**
+     * @return {!Object.<string, !WebInspector.AuditCategory>}
+     */
+    get categoriesById()
+    {
+        return this._auditCategoriesById;
+    },
+
+    /**
+     * @param {!WebInspector.AuditCategory} category
+     */
+    addCategory: function(category)
+    {
+        this.categoriesById[category.id] = category;
+        this._launcherView.addCategory(category);
+    },
+
+    /**
+     * @param {string} id
+     * @return {WebInspector.AuditCategory}
+     */
+    getCategory: function(id)
+    {
+        return this.categoriesById[id];
+    },
+
+    _constructCategories: function()
+    {
+        this._auditCategoriesById = {};
+        for (var categoryCtorID in WebInspector.AuditCategories) {
+            var auditCategory = new WebInspector.AuditCategories[categoryCtorID]();
+            auditCategory._id = categoryCtorID;
+            this.categoriesById[categoryCtorID] = auditCategory;
+        }
+    },
+
+    /**
+     * @param {string} mainResourceURL
+     * @param {!Array.<!WebInspector.AuditCategoryResult>} results
+     */
+    auditFinishedCallback: function(mainResourceURL, results)
+    {
+        var children = this.auditResultsTreeElement.children;
+        var ordinal = 1;
+        for (var i = 0; i < children.length; ++i) {
+            if (children[i].mainResourceURL === mainResourceURL)
+                ordinal++;
+        }
+
+        var resultTreeElement = new WebInspector.AuditResultSidebarTreeElement(this, results, mainResourceURL, ordinal);
+        this.auditResultsTreeElement.appendChild(resultTreeElement);
+        resultTreeElement.revealAndSelect();
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.AuditCategoryResult>} categoryResults
+     */
+    showResults: function(categoryResults)
+    {
+        if (!categoryResults._resultView)
+            categoryResults._resultView = new WebInspector.AuditResultView(categoryResults);
+
+        this.visibleView = categoryResults._resultView;
+    },
+
+    showLauncherView: function()
+    {
+        this.visibleView = this._launcherView;
+    },
+
+    get visibleView()
+    {
+        return this._visibleView;
+    },
+
+    set visibleView(x)
+    {
+        if (this._visibleView === x)
+            return;
+
+        if (this._visibleView)
+            this._visibleView.detach();
+
+        this._visibleView = x;
+
+        if (x)
+            x.show(this.viewsContainerElement);
+    },
+
+    wasShown: function()
+    {
+        WebInspector.Panel.prototype.wasShown.call(this);
+        if (!this._visibleView)
+            this.auditsItemTreeElement.select();
+    },
+
+    _clearButtonClicked: function()
+    {
+        this.auditsItemTreeElement.revealAndSelect();
+        this.auditResultsTreeElement.removeChildren();
+    },
+
+    __proto__: WebInspector.Panel.prototype
+}
+
+/**
+ * @constructor
+ * @param {string} displayName
+ */
+WebInspector.AuditCategory = function(displayName)
+{
+    this._displayName = displayName;
+    this._rules = [];
+}
+
+WebInspector.AuditCategory.prototype = {
+    /**
+     * @return {string}
+     */
+    get id()
+    {
+        // this._id value is injected at construction time.
+        return this._id;
+    },
+
+    /**
+     * @return {string}
+     */
+    get displayName()
+    {
+        return this._displayName;
+    },
+
+    /**
+     * @param {!WebInspector.AuditRule} rule
+     * @param {!WebInspector.AuditRule.Severity} severity
+     */
+    addRule: function(rule, severity)
+    {
+        rule.severity = severity;
+        this._rules.push(rule);
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {function(WebInspector.AuditRuleResult)} ruleResultCallback
+     * @param {function()} categoryDoneCallback
+     * @param {!WebInspector.Progress} progress
+     */
+    run: function(requests, ruleResultCallback, categoryDoneCallback, progress)
+    {
+        this._ensureInitialized();
+        var remainingRulesCount = this._rules.length;
+        progress.setTotalWork(remainingRulesCount);
+        function callbackWrapper(result)
+        {
+            ruleResultCallback(result);
+            progress.worked();
+            if (!--remainingRulesCount)
+                categoryDoneCallback();
+        }
+        for (var i = 0; i < this._rules.length; ++i)
+            this._rules[i].run(requests, callbackWrapper, progress);
+    },
+
+    _ensureInitialized: function()
+    {
+        if (!this._initialized) {
+            if ("initialize" in this)
+                this.initialize();
+            this._initialized = true;
+        }
+    }
+}
+
+/**
+ * @constructor
+ * @param {string} id
+ * @param {string} displayName
+ */
+WebInspector.AuditRule = function(id, displayName)
+{
+    this._id = id;
+    this._displayName = displayName;
+}
+
+/**
+ * @enum {string}
+ */
+WebInspector.AuditRule.Severity = {
+    Info: "info",
+    Warning: "warning",
+    Severe: "severe"
+}
+
+/**
+ * @type {Object.<WebInspector.AuditRule.Severity, number>}
+ */
+WebInspector.AuditRule.SeverityOrder = {
+    "info": 3,
+    "warning": 2,
+    "severe": 1
+}
+
+WebInspector.AuditRule.prototype = {
+    get id()
+    {
+        return this._id;
+    },
+
+    get displayName()
+    {
+        return this._displayName;
+    },
+
+    /**
+     * @param {WebInspector.AuditRule.Severity} severity
+     */
+    set severity(severity)
+    {
+        this._severity = severity;
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest>} requests
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {!WebInspector.Progress} progress
+     */
+    run: function(requests, callback, progress)
+    {
+        if (progress.isCanceled())
+            return;
+
+        var result = new WebInspector.AuditRuleResult(this.displayName);
+        result.severity = this._severity;
+        this.doRun(requests, result, callback, progress);
+    },
+
+    /**
+     * @param {Array.<WebInspector.NetworkRequest>} requests
+     * @param {WebInspector.AuditRuleResult} result
+     * @param {function(WebInspector.AuditRuleResult)} callback
+     * @param {WebInspector.Progress} progress
+     */
+    doRun: function(requests, result, callback, progress)
+    {
+        throw new Error("doRun() not implemented");
+    }
+}
+
+/**
+ * @constructor
+ * @param {!WebInspector.AuditCategory} category
+ */
+WebInspector.AuditCategoryResult = function(category)
+{
+    this.title = category.displayName;
+    this.ruleResults = [];
+}
+
+WebInspector.AuditCategoryResult.prototype = {
+    /**
+     * @param {!WebInspector.AuditCategoryResult} ruleResult
+     */
+    addRuleResult: function(ruleResult)
+    {
+        this.ruleResults.push(ruleResult);
+    }
+}
+
+/**
+ * @constructor
+ * @param {(string|boolean|number|Object)} value
+ * @param {boolean=} expanded
+ * @param {string=} className
+ */
+WebInspector.AuditRuleResult = function(value, expanded, className)
+{
+    this.value = value;
+    this.className = className;
+    this.expanded = expanded;
+    this.violationCount = 0;
+    this._formatters = {
+        r: WebInspector.AuditRuleResult.linkifyDisplayName
+    };
+    var standardFormatters = Object.keys(String.standardFormatters);
+    for (var i = 0; i < standardFormatters.length; ++i)
+        this._formatters[standardFormatters[i]] = String.standardFormatters[standardFormatters[i]];
+}
+
+/**
+ * @param {string} url
+ * @return {!Element}
+ */
+WebInspector.AuditRuleResult.linkifyDisplayName = function(url)
+{
+    return WebInspector.linkifyURLAsNode(url, WebInspector.displayNameForURL(url));
+}
+
+WebInspector.AuditRuleResult.resourceDomain = function(domain)
+{
+    return domain || WebInspector.UIString("[empty domain]");
+}
+
+WebInspector.AuditRuleResult.prototype = {
+    /**
+     * @param {(string|boolean|number|Object)} value
+     * @param {boolean=} expanded
+     * @param {string=} className
+     * @return {!WebInspector.AuditRuleResult}
+     */
+    addChild: function(value, expanded, className)
+    {
+        if (!this.children)
+            this.children = [];
+        var entry = new WebInspector.AuditRuleResult(value, expanded, className);
+        this.children.push(entry);
+        return entry;
+    },
+
+    /**
+     * @param {string} url
+     */
+    addURL: function(url)
+    {
+        this.addChild(WebInspector.AuditRuleResult.linkifyDisplayName(url));
+    },
+
+    /**
+     * @param {!Array.<string>} urls
+     */
+    addURLs: function(urls)
+    {
+        for (var i = 0; i < urls.length; ++i)
+            this.addURL(urls[i]);
+    },
+
+    /**
+     * @param {string} snippet
+     */
+    addSnippet: function(snippet)
+    {
+        this.addChild(snippet, false, "source-code");
+    },
+
+    /**
+     * @param {string} format
+     * @param {...*} vararg
+     * @return {!WebInspector.AuditRuleResult}
+     */
+    addFormatted: function(format, vararg)
+    {
+        var substitutions = Array.prototype.slice.call(arguments, 1);
+        var fragment = document.createDocumentFragment();
+
+        function append(a, b)
+        {
+            if (!(b instanceof Node))
+                b = document.createTextNode(b);
+            a.appendChild(b);
+            return a;
+        }
+
+        var formattedResult = String.format(format, substitutions, this._formatters, fragment, append).formattedResult;
+        if (formattedResult instanceof Node)
+            formattedResult.normalize();
+        return this.addChild(formattedResult);
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarTreeElement}
+ * @param {WebInspector.AuditsPanel} panel
+ */
+WebInspector.AuditsSidebarTreeElement = function(panel)
+{
+    this._panel = panel;
+    this.small = false;
+    WebInspector.SidebarTreeElement.call(this, "audits-sidebar-tree-item", WebInspector.UIString("Audits"), "", null, false);
+}
+
+WebInspector.AuditsSidebarTreeElement.prototype = {
+    onattach: function()
+    {
+        WebInspector.SidebarTreeElement.prototype.onattach.call(this);
+    },
+
+    onselect: function()
+    {
+        this._panel.showLauncherView();
+    },
+
+    get selectable()
+    {
+        return true;
+    },
+
+    refresh: function()
+    {
+        this.refreshTitles();
+    },
+
+    __proto__: WebInspector.SidebarTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarTreeElement}
+ * @param {!WebInspector.AuditsPanel} panel
+ * @param {!Array.<!WebInspector.AuditCategoryResult>} results
+ * @param {string} mainResourceURL
+ * @param {number} ordinal
+ */
+WebInspector.AuditResultSidebarTreeElement = function(panel, results, mainResourceURL, ordinal)
+{
+    this._panel = panel;
+    this.results = results;
+    this.mainResourceURL = mainResourceURL;
+    WebInspector.SidebarTreeElement.call(this, "audit-result-sidebar-tree-item", String.sprintf("%s (%d)", mainResourceURL, ordinal), "", {}, false);
+}
+
+WebInspector.AuditResultSidebarTreeElement.prototype = {
+    onselect: function()
+    {
+        this._panel.showResults(this.results);
+    },
+
+    get selectable()
+    {
+        return true;
+    },
+
+    __proto__: WebInspector.SidebarTreeElement.prototype
+}
+
+// Contributed audit rules should go into this namespace.
+WebInspector.AuditRules = {};
+
+/**
+ * Contributed audit categories should go into this namespace.
+ * @type {Object.<string, function(new:WebInspector.AuditCategory)>}
+ */
+WebInspector.AuditCategories = {};
+
+importScript("AuditCategories.js");
+importScript("AuditController.js");
+importScript("AuditFormatters.js");
+importScript("AuditLauncherView.js");
+importScript("AuditResultView.js");
+importScript("AuditRules.js");
diff --git a/Source/devtools/front_end/BottomUpProfileDataGridTree.js b/Source/devtools/front_end/BottomUpProfileDataGridTree.js
new file mode 100644
index 0000000..13d158b
--- /dev/null
+++ b/Source/devtools/front_end/BottomUpProfileDataGridTree.js
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2009 280 North 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. ``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
+ * 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.
+ */
+
+// Bottom Up Profiling shows the entire callstack backwards:
+// The root node is a representation of each individual function called, and each child of that node represents
+// a reverse-callstack showing how many of those calls came from it. So, unlike top-down, the statistics in
+// each child still represent the root node. We have to be particularly careful of recursion with this mode
+// because a root node can represent itself AND an ancestor.
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileDataGridNode}
+ * @param {!ProfilerAgent.CPUProfileNode} profileNode
+ * @param {!WebInspector.TopDownProfileDataGridTree} owningTree
+ */
+WebInspector.BottomUpProfileDataGridNode = function(profileNode, owningTree)
+{
+    WebInspector.ProfileDataGridNode.call(this, profileNode, owningTree, this._willHaveChildren(profileNode));
+
+    this._remainingNodeInfos = [];
+}
+
+WebInspector.BottomUpProfileDataGridNode.prototype = {
+    /**
+     * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+     */
+    _takePropertiesFromProfileDataGridNode: function(profileDataGridNode)
+    {
+        this._save();
+
+        this.selfTime = profileDataGridNode.selfTime;
+        this.totalTime = profileDataGridNode.totalTime;
+        this.numberOfCalls = profileDataGridNode.numberOfCalls;
+    },
+
+    /**
+     * When focusing, we keep just the members of the callstack.
+     * @param {!WebInspector.ProfileDataGridNode} child
+     */
+    _keepOnlyChild: function(child)
+    {
+        this._save();
+
+        this.removeChildren();
+        this.appendChild(child);
+    },
+
+    _exclude: function(aCallUID)
+    {
+        if (this._remainingNodeInfos)
+            this.populate();
+
+        this._save();
+
+        var children = this.children;
+        var index = this.children.length;
+
+        while (index--)
+            children[index]._exclude(aCallUID);
+
+        var child = this.childrenByCallUID[aCallUID];
+
+        if (child)
+            this._merge(child, true);
+    },
+
+    _restore: function()
+    {
+        WebInspector.ProfileDataGridNode.prototype._restore();
+
+        if (!this.children.length)
+            this.hasChildren = this._willHaveChildren(this.profileNode);
+    },
+
+    /**
+     * @param {!WebInspector.ProfileDataGridNode} child
+     * @param {boolean} shouldAbsorb
+     */
+    _merge: function(child, shouldAbsorb)
+    {
+        this.selfTime -= child.selfTime;
+
+        WebInspector.ProfileDataGridNode.prototype._merge.call(this, child, shouldAbsorb);
+    },
+
+    _sharedPopulate: function()
+    {
+        var remainingNodeInfos = this._remainingNodeInfos;
+        var count = remainingNodeInfos.length;
+
+        for (var index = 0; index < count; ++index) {
+            var nodeInfo = remainingNodeInfos[index];
+            var ancestor = nodeInfo.ancestor;
+            var focusNode = nodeInfo.focusNode;
+            var child = this.findChild(ancestor);
+
+            // If we already have this child, then merge the data together.
+            if (child) {
+                var totalTimeAccountedFor = nodeInfo.totalTimeAccountedFor;
+
+                child.selfTime += focusNode.selfTime;
+                child.numberOfCalls += focusNode.numberOfCalls;
+
+                if (!totalTimeAccountedFor)
+                    child.totalTime += focusNode.totalTime;
+            } else {
+                // If not, add it as a true ancestor.
+                // In heavy mode, we take our visual identity from ancestor node...
+                child = new WebInspector.BottomUpProfileDataGridNode(ancestor, this.tree);
+
+                if (ancestor !== focusNode) {
+                    // but the actual statistics from the "root" node (bottom of the callstack).
+                    child.selfTime = focusNode.selfTime;
+                    child.totalTime = focusNode.totalTime;
+                    child.numberOfCalls = focusNode.numberOfCalls;
+                }
+
+                this.appendChild(child);
+            }
+
+            var parent = ancestor.parent;
+            if (parent && parent.parent) {
+                nodeInfo.ancestor = parent;
+                child._remainingNodeInfos.push(nodeInfo);
+            }
+        }
+
+        delete this._remainingNodeInfos;
+    },
+
+    _willHaveChildren: function(profileNode)
+    {
+        // In bottom up mode, our parents are our children since we display an inverted tree.
+        // However, we don't want to show the very top parent since it is redundant.
+        return !!(profileNode.parent && profileNode.parent.parent);
+    },
+
+    __proto__: WebInspector.ProfileDataGridNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileDataGridTree}
+ * @param {WebInspector.CPUProfileView} profileView
+ * @param {ProfilerAgent.CPUProfileNode} rootProfileNode
+ */
+WebInspector.BottomUpProfileDataGridTree = function(profileView, rootProfileNode)
+{
+    WebInspector.ProfileDataGridTree.call(this, profileView, rootProfileNode);
+
+    // Iterate each node in pre-order.
+    var profileNodeUIDs = 0;
+    var profileNodeGroups = [[], [rootProfileNode]];
+    var visitedProfileNodesForCallUID = {};
+
+    this._remainingNodeInfos = [];
+
+    for (var profileNodeGroupIndex = 0; profileNodeGroupIndex < profileNodeGroups.length; ++profileNodeGroupIndex) {
+        var parentProfileNodes = profileNodeGroups[profileNodeGroupIndex];
+        var profileNodes = profileNodeGroups[++profileNodeGroupIndex];
+        var count = profileNodes.length;
+
+        for (var index = 0; index < count; ++index) {
+            var profileNode = profileNodes[index];
+
+            if (!profileNode.UID)
+                profileNode.UID = ++profileNodeUIDs;
+
+            if (profileNode.head && profileNode !== profileNode.head) {
+                // The total time of this ancestor is accounted for if we're in any form of recursive cycle.
+                var visitedNodes = visitedProfileNodesForCallUID[profileNode.callUID];
+                var totalTimeAccountedFor = false;
+
+                if (!visitedNodes) {
+                    visitedNodes = {}
+                    visitedProfileNodesForCallUID[profileNode.callUID] = visitedNodes;
+                } else {
+                    // The total time for this node has already been accounted for iff one of it's parents has already been visited.
+                    // We can do this check in this style because we are traversing the tree in pre-order.
+                    var parentCount = parentProfileNodes.length;
+                    for (var parentIndex = 0; parentIndex < parentCount; ++parentIndex) {
+                        if (visitedNodes[parentProfileNodes[parentIndex].UID]) {
+                            totalTimeAccountedFor = true;
+                            break;
+                        }
+                    }
+                }
+
+                visitedNodes[profileNode.UID] = true;
+
+                this._remainingNodeInfos.push({ ancestor:profileNode, focusNode:profileNode, totalTimeAccountedFor:totalTimeAccountedFor });
+            }
+
+            var children = profileNode.children;
+            if (children.length) {
+                profileNodeGroups.push(parentProfileNodes.concat([profileNode]))
+                profileNodeGroups.push(children);
+            }
+        }
+    }
+
+    // Populate the top level nodes.
+    var any = /** @type{*} */(this);
+    var node = /** @type{WebInspector.ProfileDataGridNode} */(any);
+    WebInspector.BottomUpProfileDataGridNode.prototype.populate.call(node);
+
+    return this;
+}
+
+WebInspector.BottomUpProfileDataGridTree.prototype = {
+    /**
+     * When focusing, we keep the entire callstack up to this ancestor.
+     * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+     */
+    focus: function(profileDataGridNode)
+    {
+        if (!profileDataGridNode)
+            return;
+
+        this._save();
+
+        var currentNode = profileDataGridNode;
+        var focusNode = profileDataGridNode;
+
+        while (currentNode.parent && (currentNode instanceof WebInspector.ProfileDataGridNode)) {
+            currentNode._takePropertiesFromProfileDataGridNode(profileDataGridNode);
+
+            focusNode = currentNode;
+            currentNode = currentNode.parent;
+
+            if (currentNode instanceof WebInspector.ProfileDataGridNode)
+                currentNode._keepOnlyChild(focusNode);
+        }
+
+        this.children = [focusNode];
+        this.totalTime = profileDataGridNode.totalTime;
+    },
+
+    /**
+     * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+     */
+    exclude: function(profileDataGridNode)
+    {
+        if (!profileDataGridNode)
+            return;
+
+        this._save();
+
+        var excludedCallUID = profileDataGridNode.callUID;
+        var excludedTopLevelChild = this.childrenByCallUID[excludedCallUID];
+
+        // If we have a top level node that is excluded, get rid of it completely (not keeping children),
+        // since bottom up data relies entirely on the root node.
+        if (excludedTopLevelChild)
+            this.children.remove(excludedTopLevelChild);
+
+        var children = this.children;
+        var count = children.length;
+
+        for (var index = 0; index < count; ++index)
+            children[index]._exclude(excludedCallUID);
+
+        if (this.lastComparator)
+            this.sort(this.lastComparator, true);
+    },
+
+    _sharedPopulate: WebInspector.BottomUpProfileDataGridNode.prototype._sharedPopulate,
+
+    __proto__: WebInspector.ProfileDataGridTree.prototype
+}
diff --git a/Source/devtools/front_end/BreakpointManager.js b/Source/devtools/front_end/BreakpointManager.js
new file mode 100644
index 0000000..02910f7
--- /dev/null
+++ b/Source/devtools/front_end/BreakpointManager.js
@@ -0,0 +1,669 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.Setting} breakpointStorage
+ * @param {WebInspector.DebuggerModel} debuggerModel
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.BreakpointManager = function(breakpointStorage, debuggerModel, workspace)
+{
+    this._storage = new WebInspector.BreakpointManager.Storage(this, breakpointStorage);
+    this._debuggerModel = debuggerModel;
+    this._workspace = workspace;
+
+    this._breakpoints = new Map();
+    this._breakpointForDebuggerId = {};
+    this._breakpointsForUISourceCode = new Map();
+    this._sourceFilesWithRestoredBreakpoints = {};
+
+    this._debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointResolved, this._breakpointResolved, this);
+    this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset, this);
+    this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
+}
+
+WebInspector.BreakpointManager.Events = {
+    BreakpointAdded: "breakpoint-added",
+    BreakpointRemoved: "breakpoint-removed"
+}
+
+WebInspector.BreakpointManager.sourceFileId = function(uiSourceCode)
+{
+    if (!uiSourceCode.url)
+        return "";
+    var deobfuscatedPrefix = uiSourceCode.formatted() ? "deobfuscated:" : "";
+    return deobfuscatedPrefix + uiSourceCode.uri();
+}
+
+WebInspector.BreakpointManager.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _restoreBreakpoints: function(uiSourceCode)
+    {
+        var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
+        if (!sourceFileId || this._sourceFilesWithRestoredBreakpoints[sourceFileId])
+            return;
+        this._sourceFilesWithRestoredBreakpoints[sourceFileId] = true;
+
+        // Erase provisional breakpoints prior to restoring them.
+        for (var debuggerId in this._breakpointForDebuggerId) {
+            var breakpoint = this._breakpointForDebuggerId[debuggerId];
+            if (breakpoint._sourceFileId !== sourceFileId)
+                continue;
+            breakpoint.remove(true);
+        }
+        this._storage._restoreBreakpoints(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _uiSourceCodeAdded: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        this._restoreBreakpoints(uiSourceCode);
+        if (uiSourceCode.contentType() === WebInspector.resourceTypes.Script || uiSourceCode.contentType() === WebInspector.resourceTypes.Document) {
+            uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._uiSourceCodeMappingChanged, this);
+            uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormatted, this);
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _uiSourceCodeFormatted: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
+        this._restoreBreakpoints(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _resetBreakpoints: function(uiSourceCode)
+    {
+        var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
+        var breakpoints = this._breakpoints.keys();
+        for (var i = 0; i < breakpoints.length; ++i) {
+            var breakpoint = breakpoints[i];
+            if (breakpoint._sourceFileId !== sourceFileId)
+                return;
+            if (breakpoint.enabled()) {
+                breakpoint._removeFromDebugger();
+                breakpoint._setInDebugger();
+            }
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _uiSourceCodeMappingChanged: function(event)
+    {
+        var identityHasChanged = /** @type {boolean} */ (event.data.identityHasChanged);
+        if (!identityHasChanged)
+            return;
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
+        this._resetBreakpoints(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {string} condition
+     * @param {boolean} enabled
+     * @return {WebInspector.BreakpointManager.Breakpoint}
+     */
+    setBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
+    {
+        this._debuggerModel.setBreakpointsActive(true);
+        return this._innerSetBreakpoint(uiSourceCode, lineNumber, condition, enabled);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {string} condition
+     * @param {boolean} enabled
+     * @return {WebInspector.BreakpointManager.Breakpoint}
+     */
+    _innerSetBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
+    {
+        var breakpoint = this.findBreakpoint(uiSourceCode, lineNumber);
+        if (breakpoint) {
+            breakpoint._updateBreakpoint(condition, enabled);
+            return breakpoint;
+        }
+        breakpoint = new WebInspector.BreakpointManager.Breakpoint(this, uiSourceCode, lineNumber, condition, enabled);
+        this._breakpoints.put(breakpoint);
+        return breakpoint;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @return {?WebInspector.BreakpointManager.Breakpoint}
+     */
+    findBreakpoint: function(uiSourceCode, lineNumber)
+    {
+        var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
+        var lineBreakpoints = breakpoints ? breakpoints[lineNumber] : null;
+        return lineBreakpoints ? lineBreakpoints[0] : null;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {Array.<WebInspector.BreakpointManager.Breakpoint>}
+     */
+    breakpointsForUISourceCode: function(uiSourceCode)
+    {
+        var result = [];
+        var breakpoints = /** @type {Array.<WebInspector.BreakpointManager.Breakpoint>} */(this._breakpoints.keys());
+        for (var i = 0; i < breakpoints.length; ++i) {
+            var breakpoint = breakpoints[i];
+            var uiLocation = breakpoint._primaryUILocation;
+            if (uiLocation.uiSourceCode === uiSourceCode)
+                result.push(breakpoint);
+        }
+        return result;
+    },
+
+    /**
+     * @return {Array.<WebInspector.BreakpointManager.Breakpoint>}
+     */
+    allBreakpoints: function()
+    {
+        var result = [];
+        var breakpoints = /** @type {Array.<WebInspector.BreakpointManager.Breakpoint>} */(this._breakpoints.keys());
+        return breakpoints;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {Array.<{breakpoint: WebInspector.BreakpointManager.Breakpoint, uiLocation: WebInspector.UILocation}>}
+     */
+    breakpointLocationsForUISourceCode: function(uiSourceCode)
+    {
+        var result = [];
+        var breakpoints = /** @type {Array.<WebInspector.BreakpointManager.Breakpoint>} */(this._breakpoints.keys());
+        for (var i = 0; i < breakpoints.length; ++i) {
+            var breakpoint = breakpoints[i];
+            var uiLocations = Object.values(breakpoint._uiLocations);
+            for (var j = 0; j < uiLocations.length; ++j) {
+                var uiLocation = uiLocations[j];
+                if (uiLocation.uiSourceCode === uiSourceCode)
+                    result.push({breakpoint: breakpoint, uiLocation: uiLocations[j]});
+            }
+        }
+        return result;
+    },
+
+    /**
+     * @return {Array.<{breakpoint: WebInspector.BreakpointManager.Breakpoint, uiLocation: WebInspector.UILocation}>}
+     */
+    allBreakpointLocations: function()
+    {
+        var result = [];
+        var breakpoints = /** @type {Array.<WebInspector.BreakpointManager.Breakpoint>} */(this._breakpoints.keys());
+        for (var i = 0; i < breakpoints.length; ++i) {
+            var breakpoint = breakpoints[i];
+            var uiLocations = Object.values(breakpoint._uiLocations);
+            for (var j = 0; j < uiLocations.length; ++j)
+                result.push({breakpoint: breakpoint, uiLocation: uiLocations[j]});
+        }
+        return result;
+    },
+
+    /**
+     * @param {boolean} toggleState
+     */
+    toggleAllBreakpoints: function(toggleState)
+    {
+        var breakpoints = /** @type {Array.<WebInspector.BreakpointManager.Breakpoint>} */(this._breakpoints.keys());
+        for (var i = 0; i < breakpoints.length; ++i) {
+            var breakpoint = breakpoints[i];
+            if (breakpoint.enabled() != toggleState)
+                breakpoint.setEnabled(toggleState);
+        }
+    },
+
+    removeAllBreakpoints: function()
+    {
+        var breakpoints = /** @type {Array.<WebInspector.BreakpointManager.Breakpoint>} */(this._breakpoints.keys());
+        for (var i = 0; i < breakpoints.length; ++i)
+            breakpoints[i].remove();
+    },
+
+    reset: function()
+    {
+        // Remove all breakpoints from UI and debugger, do not update storage.
+        this._storage._muted = true;
+        this.removeAllBreakpoints();
+        delete this._storage._muted;
+
+        // Remove all provisional breakpoints from the debugger.
+        for (var debuggerId in this._breakpointForDebuggerId)
+            this._debuggerModel.removeBreakpoint(debuggerId);
+        this._breakpointForDebuggerId = {};
+        this._sourceFilesWithRestoredBreakpoints = {};
+    },
+
+    _projectWillReset: function(event)
+    {
+        var project = /** @type {WebInspector.Project} */ (event.data);
+        var uiSourceCodes = project.uiSourceCodes();
+        for (var i = 0; i < uiSourceCodes.length; ++i) {
+            var uiSourceCode = uiSourceCodes[i];
+            var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode) || [];
+            for (var lineNumber in breakpoints) {
+                var lineBreakpoints = breakpoints[lineNumber];
+                for (var j = 0; j < lineBreakpoints.length; ++j) {
+                    var breakpoint = lineBreakpoints[j];
+                    breakpoint._resetLocations();
+                }
+            }
+            this._breakpointsForUISourceCode.remove(uiSourceCode);
+
+            breakpoints = this.breakpointsForUISourceCode(uiSourceCode);
+            for (var j = 0; j < breakpoints.length; ++j) { 
+                var breakpoint = breakpoints[j];
+                this._breakpoints.remove(breakpoint);
+            }
+
+            var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
+            delete this._sourceFilesWithRestoredBreakpoints[sourceFileId];
+        }
+    },
+
+    _breakpointResolved: function(event)
+    {
+        var breakpointId = /** @type {DebuggerAgent.BreakpointId} */ (event.data.breakpointId);
+        var location = /** @type {WebInspector.DebuggerModel.Location} */ (event.data.location);
+        var breakpoint = this._breakpointForDebuggerId[breakpointId];
+        if (!breakpoint)
+            return;
+        if (!this._breakpoints.contains(breakpoint))
+            this._breakpoints.put(breakpoint);
+        breakpoint._addResolvedLocation(location);
+    },
+
+    /**
+     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+     * @param {boolean} removeFromStorage
+     */
+    _removeBreakpoint: function(breakpoint, removeFromStorage)
+    {
+        console.assert(!breakpoint._debuggerId)
+        this._breakpoints.remove(breakpoint);
+        if (removeFromStorage)
+            this._storage._removeBreakpoint(breakpoint);
+    },
+
+    /**
+     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    _uiLocationAdded: function(breakpoint, uiLocation)
+    {
+        var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
+        if (!breakpoints) {
+            breakpoints = {};
+            this._breakpointsForUISourceCode.put(uiLocation.uiSourceCode, breakpoints);
+        }
+
+        var lineBreakpoints = breakpoints[uiLocation.lineNumber];
+        if (!lineBreakpoints) {
+            lineBreakpoints = [];
+            breakpoints[uiLocation.lineNumber] = lineBreakpoints;
+        }
+
+        lineBreakpoints.push(breakpoint);
+        this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointAdded, {breakpoint: breakpoint, uiLocation: uiLocation});
+    },
+
+    /**
+     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    _uiLocationRemoved: function(breakpoint, uiLocation)
+    {
+      var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
+        if (!breakpoints)
+            return;
+
+        var lineBreakpoints = breakpoints[uiLocation.lineNumber];
+        if (!lineBreakpoints)
+            return;
+
+        lineBreakpoints.remove(breakpoint);
+        if (!lineBreakpoints.length)
+            delete breakpoints[uiLocation.lineNumber];
+        this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointRemoved, {breakpoint: breakpoint, uiLocation: uiLocation});
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.BreakpointManager} breakpointManager
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ * @param {number} lineNumber
+ * @param {string} condition
+ * @param {boolean} enabled
+ */
+WebInspector.BreakpointManager.Breakpoint = function(breakpointManager, uiSourceCode, lineNumber, condition, enabled)
+{
+    this._breakpointManager = breakpointManager;
+    this._primaryUILocation = new WebInspector.UILocation(uiSourceCode, lineNumber, 0);
+    this._sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
+    /** @type {Array.<WebInspector.Script.Location>} */
+    this._liveLocations = [];
+    /** @type {Object.<string, WebInspector.UILocation>} */
+    this._uiLocations = {};
+
+    // Force breakpoint update.
+    /** @type {string} */ this._condition;
+    /** @type {boolean} */ this._enabled;
+    this._updateBreakpoint(condition, enabled);
+}
+
+WebInspector.BreakpointManager.Breakpoint.prototype = {
+    /**
+     * @return {WebInspector.UILocation}
+     */
+    primaryUILocation: function()
+    {
+        return this._primaryUILocation;
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} location
+     */
+    _addResolvedLocation: function(location)
+    {
+        this._liveLocations.push(this._breakpointManager._debuggerModel.createLiveLocation(location, this._locationUpdated.bind(this, location)));
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} location
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    _locationUpdated: function(location, uiLocation)
+    {
+        var stringifiedLocation = location.scriptId + ":" + location.lineNumber + ":" + location.columnNumber;
+        var oldUILocation = /** @type {WebInspector.UILocation} */ (this._uiLocations[stringifiedLocation]);
+        if (oldUILocation)
+            this._breakpointManager._uiLocationRemoved(this, oldUILocation);
+        if (this._uiLocations[""]) {
+            delete this._uiLocations[""];
+            this._breakpointManager._uiLocationRemoved(this, this._primaryUILocation);
+        }
+        this._uiLocations[stringifiedLocation] = uiLocation;
+        this._breakpointManager._uiLocationAdded(this, uiLocation);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    enabled: function()
+    {
+        return this._enabled;
+    },
+
+    /**
+     * @param {boolean} enabled
+     */
+    setEnabled: function(enabled)
+    {
+        this._updateBreakpoint(this._condition, enabled);
+    },
+
+    /**
+     * @return {string}
+     */
+    condition: function()
+    {
+        return this._condition;
+    },
+
+    /**
+     * @param {string} condition
+     */
+    setCondition: function(condition)
+    {
+        this._updateBreakpoint(condition, this._enabled);
+    },
+
+    /**
+     * @param {string} condition
+     * @param {boolean} enabled
+     */
+    _updateBreakpoint: function(condition, enabled)
+    {
+        if (this._enabled === enabled && this._condition === condition)
+            return;
+
+        if (this._enabled)
+            this._removeFromDebugger();
+
+        this._enabled = enabled;
+        this._condition = condition;
+        this._breakpointManager._storage._updateBreakpoint(this);
+
+        var scriptFile = this._primaryUILocation.uiSourceCode.scriptFile();
+        if (this._enabled && !(scriptFile && scriptFile.hasDivergedFromVM())) {
+            this._setInDebugger();
+            return;
+        }
+
+        this._fakeBreakpointAtPrimaryLocation();
+    },
+
+    /**
+     * @param {boolean=} keepInStorage
+     */
+    remove: function(keepInStorage)
+    {
+        var removeFromStorage = !keepInStorage;
+        this._resetLocations();
+        this._removeFromDebugger();
+        this._breakpointManager._removeBreakpoint(this, removeFromStorage);
+    },
+
+    _setInDebugger: function()
+    {
+        console.assert(!this._debuggerId);
+        var rawLocation = this._primaryUILocation.uiLocationToRawLocation();
+        var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation);
+        if (debuggerModelLocation)
+            this._breakpointManager._debuggerModel.setBreakpointByScriptLocation(debuggerModelLocation, this._condition, didSetBreakpoint.bind(this));
+        else
+            this._breakpointManager._debuggerModel.setBreakpointByURL(this._primaryUILocation.uiSourceCode.url, this._primaryUILocation.lineNumber, 0, this._condition, didSetBreakpoint.bind(this));
+
+        /**
+         * @this {WebInspector.BreakpointManager.Breakpoint}
+         * @param {?DebuggerAgent.BreakpointId} breakpointId
+         * @param {Array.<WebInspector.DebuggerModel.Location>} locations
+         */
+        function didSetBreakpoint(breakpointId, locations)
+        {
+            if (!breakpointId) {
+                this._resetLocations();
+                this._breakpointManager._removeBreakpoint(this, false);
+                return;
+            }
+
+            this._debuggerId = breakpointId;
+            this._breakpointManager._breakpointForDebuggerId[breakpointId] = this;
+
+            if (!locations.length) {
+                this._fakeBreakpointAtPrimaryLocation();
+                return;
+            }
+
+            this._resetLocations();
+            for (var i = 0; i < locations.length; ++i) {
+                var script = this._breakpointManager._debuggerModel.scriptForId(locations[i].scriptId);
+                var uiLocation = script.rawLocationToUILocation(locations[i].lineNumber, locations[i].columnNumber);
+                if (this._breakpointManager.findBreakpoint(uiLocation.uiSourceCode, uiLocation.lineNumber)) {
+                    // location clash
+                    this.remove();
+                    return;
+                }
+            }
+
+            for (var i = 0; i < locations.length; ++i)
+                this._addResolvedLocation(locations[i]);
+        }
+    },
+
+    _removeFromDebugger: function()
+    {
+        if (this._debuggerId) {
+            this._breakpointManager._debuggerModel.removeBreakpoint(this._debuggerId);
+            delete this._breakpointManager._breakpointForDebuggerId[this._debuggerId];
+            delete this._debuggerId;
+        }
+    },
+
+    _resetLocations: function()
+    {
+        for (var stringifiedLocation in this._uiLocations)
+            this._breakpointManager._uiLocationRemoved(this, this._uiLocations[stringifiedLocation]);
+
+        for (var i = 0; i < this._liveLocations.length; ++i)
+            this._liveLocations[i].dispose();
+        this._liveLocations = [];
+
+        this._uiLocations = {};
+    },
+
+    /**
+     * @return {string}
+     */
+    _breakpointStorageId: function()
+    {
+        if (!this._sourceFileId)
+            return "";
+        return this._sourceFileId + ":" + this._primaryUILocation.lineNumber;
+    },
+
+    _fakeBreakpointAtPrimaryLocation: function()
+    {
+        this._resetLocations();
+        this._uiLocations[""] = this._primaryUILocation;
+        this._breakpointManager._uiLocationAdded(this, this._primaryUILocation);
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.BreakpointManager} breakpointManager
+ * @param {WebInspector.Setting} setting
+ */
+WebInspector.BreakpointManager.Storage = function(breakpointManager, setting)
+{
+    this._breakpointManager = breakpointManager;
+    this._setting = setting;
+    var breakpoints = this._setting.get();
+    /** @type {Object.<string,WebInspector.BreakpointManager.Storage.Item>} */
+    this._breakpoints = {};
+    for (var i = 0; i < breakpoints.length; ++i) {
+        var breakpoint = /** @type {WebInspector.BreakpointManager.Storage.Item} */ (breakpoints[i]);
+        this._breakpoints[breakpoint.sourceFileId + ":" + breakpoint.lineNumber] = breakpoint;
+    }
+}
+
+WebInspector.BreakpointManager.Storage.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _restoreBreakpoints: function(uiSourceCode)
+    {
+        this._muted = true;
+        var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
+        for (var id in this._breakpoints) {
+            var breakpoint = this._breakpoints[id];
+            if (breakpoint.sourceFileId === sourceFileId)
+                this._breakpointManager._innerSetBreakpoint(uiSourceCode, breakpoint.lineNumber, breakpoint.condition, breakpoint.enabled);
+        }
+        delete this._muted;
+    },
+
+    /**
+     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+     */
+    _updateBreakpoint: function(breakpoint)
+    {
+        if (this._muted || !breakpoint._breakpointStorageId())
+            return;
+        this._breakpoints[breakpoint._breakpointStorageId()] = new WebInspector.BreakpointManager.Storage.Item(breakpoint);
+        this._save();
+    },
+
+    /**
+     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+     */
+    _removeBreakpoint: function(breakpoint)
+    {
+        if (this._muted)
+            return;
+        delete this._breakpoints[breakpoint._breakpointStorageId()];
+        this._save();
+    },
+
+    _save: function()
+    {
+        var breakpointsArray = [];
+        for (var id in this._breakpoints)
+            breakpointsArray.push(this._breakpoints[id]);
+        this._setting.set(breakpointsArray);
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+ */
+WebInspector.BreakpointManager.Storage.Item = function(breakpoint)
+{
+    var primaryUILocation = breakpoint.primaryUILocation();
+    this.sourceFileId = breakpoint._sourceFileId;
+    this.lineNumber = primaryUILocation.lineNumber;
+    this.condition = breakpoint.condition();
+    this.enabled = breakpoint.enabled();
+}
+
+/** @type {WebInspector.BreakpointManager} */
+WebInspector.breakpointManager = null;
diff --git a/Source/devtools/front_end/BreakpointsSidebarPane.js b/Source/devtools/front_end/BreakpointsSidebarPane.js
new file mode 100644
index 0000000..491bc67
--- /dev/null
+++ b/Source/devtools/front_end/BreakpointsSidebarPane.js
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.BreakpointManager} breakpointManager
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.JavaScriptBreakpointsSidebarPane = function(breakpointManager, showSourceLineDelegate)
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Breakpoints"));
+    this.registerRequiredCSS("breakpointsList.css");
+
+    this._breakpointManager = breakpointManager;
+    this._showSourceLineDelegate = showSourceLineDelegate;
+
+    this.listElement = document.createElement("ol");
+    this.listElement.className = "breakpoint-list";
+
+    this.emptyElement = document.createElement("div");
+    this.emptyElement.className = "info";
+    this.emptyElement.textContent = WebInspector.UIString("No Breakpoints");
+
+    this.bodyElement.appendChild(this.emptyElement);
+
+    this._items = new Map();
+    
+    var breakpointLocations = this._breakpointManager.allBreakpointLocations();
+    for (var i = 0; i < breakpointLocations.length; ++i)
+        this._addBreakpoint(breakpointLocations[i].breakpoint, breakpointLocations[i].uiLocation);
+
+    this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this);
+    this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this);
+
+    this.emptyElement.addEventListener("contextmenu", this._emptyElementContextMenu.bind(this), true);
+}
+
+WebInspector.JavaScriptBreakpointsSidebarPane.prototype = {
+    _emptyElementContextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        var breakpointActive = WebInspector.debuggerModel.breakpointsActive();
+        var breakpointActiveTitle = breakpointActive ?
+            WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Deactivate breakpoints" : "Deactivate Breakpoints") :
+            WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Activate breakpoints" : "Activate Breakpoints");
+        contextMenu.appendItem(breakpointActiveTitle, WebInspector.debuggerModel.setBreakpointsActive.bind(WebInspector.debuggerModel, !breakpointActive));
+        contextMenu.show();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _breakpointAdded: function(event)
+    {
+        this._breakpointRemoved(event);
+
+        var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint);
+        var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation);
+        this._addBreakpoint(breakpoint, uiLocation);
+    },
+
+    /**
+     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    _addBreakpoint: function(breakpoint, uiLocation)
+    {
+        var element = document.createElement("li");
+        element.addStyleClass("cursor-pointer");
+        element.addEventListener("contextmenu", this._breakpointContextMenu.bind(this, breakpoint), true);
+        element.addEventListener("click", this._breakpointClicked.bind(this, uiLocation), false);
+
+        var checkbox = document.createElement("input");
+        checkbox.className = "checkbox-elem";
+        checkbox.type = "checkbox";
+        checkbox.checked = breakpoint.enabled();
+        checkbox.addEventListener("click", this._breakpointCheckboxClicked.bind(this, breakpoint), false);
+        element.appendChild(checkbox);
+
+        var labelElement = document.createTextNode(uiLocation.linkText());
+        element.appendChild(labelElement);
+
+        var snippetElement = document.createElement("div");
+        snippetElement.className = "source-text monospace";
+        element.appendChild(snippetElement);
+
+        /**
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function didRequestContent(content, contentEncoded, mimeType)
+        {
+            var lineEndings = content.lineEndings();
+            if (uiLocation.lineNumber < lineEndings.length)
+                snippetElement.textContent = content.substring(lineEndings[uiLocation.lineNumber - 1], lineEndings[uiLocation.lineNumber]);
+        }
+        uiLocation.uiSourceCode.requestContent(didRequestContent.bind(this));
+
+        element._data = uiLocation;
+        var currentElement = this.listElement.firstChild;
+        while (currentElement) {
+            if (currentElement._data && this._compareBreakpoints(currentElement._data, element._data) > 0)
+                break;
+            currentElement = currentElement.nextSibling;
+        }
+        this._addListElement(element, currentElement);
+
+        var breakpointItem = {};
+        breakpointItem.element = element;
+        breakpointItem.checkbox = checkbox;
+        this._items.put(breakpoint, breakpointItem);
+
+        this.expand();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _breakpointRemoved: function(event)
+    {
+        var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint);
+        var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation);
+        var breakpointItem = this._items.get(breakpoint);
+        if (!breakpointItem)
+            return;
+        this._items.remove(breakpoint);
+        this._removeListElement(breakpointItem.element);
+    },
+
+    /**
+     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+     */
+    highlightBreakpoint: function(breakpoint)
+    {
+        var breakpointItem = this._items.get(breakpoint);
+        if (!breakpointItem)
+            return;
+        breakpointItem.element.addStyleClass("breakpoint-hit");
+        this._highlightedBreakpointItem = breakpointItem;
+    },
+
+    clearBreakpointHighlight: function()
+    {
+        if (this._highlightedBreakpointItem) {
+            this._highlightedBreakpointItem.element.removeStyleClass("breakpoint-hit");
+            delete this._highlightedBreakpointItem;
+        }
+    },
+
+    _breakpointClicked: function(uiLocation, event)
+    {
+        this._showSourceLineDelegate(uiLocation.uiSourceCode, uiLocation.lineNumber);
+    },
+
+    /**
+     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+     */
+    _breakpointCheckboxClicked: function(breakpoint, event)
+    {
+        // Breakpoint element has it's own click handler.
+        event.consume();
+        breakpoint.setEnabled(event.target.checked);
+    },
+
+    /**
+     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
+     */
+    _breakpointContextMenu: function(breakpoint, event)
+    {
+        var breakpoints = this._items.values();
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove breakpoint" : "Remove Breakpoint"), breakpoint.remove.bind(breakpoint));
+        if (breakpoints.length > 1) {
+            var removeAllTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all breakpoints" : "Remove All Breakpoints");
+            contextMenu.appendItem(removeAllTitle, this._breakpointManager.removeAllBreakpoints.bind(this._breakpointManager));
+        }
+
+        contextMenu.appendSeparator();
+        var breakpointActive = WebInspector.debuggerModel.breakpointsActive();
+        var breakpointActiveTitle = breakpointActive ?
+            WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Deactivate breakpoints" : "Deactivate Breakpoints") :
+            WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Activate breakpoints" : "Activate Breakpoints");
+        contextMenu.appendItem(breakpointActiveTitle, WebInspector.debuggerModel.setBreakpointsActive.bind(WebInspector.debuggerModel, !breakpointActive));
+
+        function enabledBreakpointCount(breakpoints)
+        {
+            var count = 0;
+            for (var i = 0; i < breakpoints.length; ++i) {
+                if (breakpoints[i].checkbox.checked)
+                    count++;
+            }
+            return count;
+        }
+        if (breakpoints.length > 1) {
+            var enableBreakpointCount = enabledBreakpointCount(breakpoints);
+            var enableTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Enable all breakpoints" : "Enable All Breakpoints");
+            var disableTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Disable all breakpoints" : "Disable All Breakpoints");
+
+            contextMenu.appendSeparator();
+
+            contextMenu.appendItem(enableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, true), !(enableBreakpointCount != breakpoints.length));
+            contextMenu.appendItem(disableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, false), !(enableBreakpointCount > 1));
+        }
+
+        contextMenu.show();
+    },
+
+    _addListElement: function(element, beforeElement)
+    {
+        if (beforeElement)
+            this.listElement.insertBefore(element, beforeElement);
+        else {
+            if (!this.listElement.firstChild) {
+                this.bodyElement.removeChild(this.emptyElement);
+                this.bodyElement.appendChild(this.listElement);
+            }
+            this.listElement.appendChild(element);
+        }
+    },
+
+    _removeListElement: function(element)
+    {
+        this.listElement.removeChild(element);
+        if (!this.listElement.firstChild) {
+            this.bodyElement.removeChild(this.listElement);
+            this.bodyElement.appendChild(this.emptyElement);
+        }
+    },
+
+    _compare: function(x, y)
+    {
+        if (x !== y)
+            return x < y ? -1 : 1;
+        return 0;
+    },
+
+    _compareBreakpoints: function(b1, b2)
+    {
+        return this._compare(b1.uiSourceCode.originURL(), b2.uiSourceCode.originURL()) || this._compare(b1.lineNumber, b2.lineNumber);
+    },
+
+    reset: function()
+    {
+        this.listElement.removeChildren();
+        if (this.listElement.parentElement) {
+            this.bodyElement.removeChild(this.listElement);
+            this.bodyElement.appendChild(this.emptyElement);
+        }
+        this._items.clear();
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NativeBreakpointsSidebarPane}
+ */
+WebInspector.XHRBreakpointsSidebarPane = function()
+{
+    WebInspector.NativeBreakpointsSidebarPane.call(this, WebInspector.UIString("XHR Breakpoints"));
+
+    this._breakpointElements = {};
+
+    var addButton = document.createElement("button");
+    addButton.className = "pane-title-button add";
+    addButton.addEventListener("click", this._addButtonClicked.bind(this), false);
+    addButton.title = WebInspector.UIString("Add XHR breakpoint");
+    this.titleElement.appendChild(addButton);
+
+    this.emptyElement.addEventListener("contextmenu", this._emptyElementContextMenu.bind(this), true);
+
+    this._restoreBreakpoints();
+}
+
+WebInspector.XHRBreakpointsSidebarPane.prototype = {
+    _emptyElementContextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add breakpoint" : "Add Breakpoint"), this._addButtonClicked.bind(this));
+        contextMenu.show();
+    },
+
+    _addButtonClicked: function(event)
+    {
+        if (event)
+            event.consume();
+
+        this.expand();
+
+        var inputElementContainer = document.createElement("p");
+        inputElementContainer.className = "breakpoint-condition";
+        var inputElement = document.createElement("span");
+        inputElementContainer.textContent = WebInspector.UIString("Break when URL contains:");
+        inputElement.className = "editing";
+        inputElement.id = "breakpoint-condition-input";
+        inputElementContainer.appendChild(inputElement);
+        this._addListElement(inputElementContainer, this.listElement.firstChild);
+
+        function finishEditing(accept, e, text)
+        {
+            this._removeListElement(inputElementContainer);
+            if (accept) {
+                this._setBreakpoint(text, true);
+                this._saveBreakpoints();
+            }
+        }
+
+        var config = new WebInspector.EditingConfig(finishEditing.bind(this, true), finishEditing.bind(this, false));
+        WebInspector.startEditing(inputElement, config);
+    },
+
+    _setBreakpoint: function(url, enabled)
+    {
+        if (url in this._breakpointElements)
+            return;
+
+        var element = document.createElement("li");
+        element._url = url;
+        element.addEventListener("contextmenu", this._contextMenu.bind(this, url), true);
+
+        var checkboxElement = document.createElement("input");
+        checkboxElement.className = "checkbox-elem";
+        checkboxElement.type = "checkbox";
+        checkboxElement.checked = enabled;
+        checkboxElement.addEventListener("click", this._checkboxClicked.bind(this, url), false);
+        element._checkboxElement = checkboxElement;
+        element.appendChild(checkboxElement);
+
+        var labelElement = document.createElement("span");
+        if (!url)
+            labelElement.textContent = WebInspector.UIString("Any XHR");
+        else
+            labelElement.textContent = WebInspector.UIString("URL contains \"%s\"", url);
+        labelElement.addStyleClass("cursor-auto");
+        labelElement.addEventListener("dblclick", this._labelClicked.bind(this, url), false);
+        element.appendChild(labelElement);
+
+        var currentElement = this.listElement.firstChild;
+        while (currentElement) {
+            if (currentElement._url && currentElement._url < element._url)
+                break;
+            currentElement = currentElement.nextSibling;
+        }
+        this._addListElement(element, currentElement);
+        this._breakpointElements[url] = element;
+        if (enabled)
+            DOMDebuggerAgent.setXHRBreakpoint(url);
+    },
+
+    _removeBreakpoint: function(url)
+    {
+        var element = this._breakpointElements[url];
+        if (!element)
+            return;
+
+        this._removeListElement(element);
+        delete this._breakpointElements[url];
+        if (element._checkboxElement.checked)
+            DOMDebuggerAgent.removeXHRBreakpoint(url);
+    },
+
+    _contextMenu: function(url, event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        function removeBreakpoint()
+        {
+            this._removeBreakpoint(url);
+            this._saveBreakpoints();
+        }
+        function removeAllBreakpoints()
+        {
+            for (var url in this._breakpointElements)
+                this._removeBreakpoint(url);
+            this._saveBreakpoints();
+        }
+        var removeAllTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all breakpoints" : "Remove All Breakpoints");
+
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add breakpoint" : "Add Breakpoint"), this._addButtonClicked.bind(this));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove breakpoint" : "Remove Breakpoint"), removeBreakpoint.bind(this));
+        contextMenu.appendItem(removeAllTitle, removeAllBreakpoints.bind(this));
+        contextMenu.show();
+    },
+
+    _checkboxClicked: function(url, event)
+    {
+        if (event.target.checked)
+            DOMDebuggerAgent.setXHRBreakpoint(url);
+        else
+            DOMDebuggerAgent.removeXHRBreakpoint(url);
+        this._saveBreakpoints();
+    },
+
+    _labelClicked: function(url)
+    {
+        var element = this._breakpointElements[url];
+        var inputElement = document.createElement("span");
+        inputElement.className = "breakpoint-condition editing";
+        inputElement.textContent = url;
+        this.listElement.insertBefore(inputElement, element);
+        element.addStyleClass("hidden");
+
+        function finishEditing(accept, e, text)
+        {
+            this._removeListElement(inputElement);
+            if (accept) {
+                this._removeBreakpoint(url);
+                this._setBreakpoint(text, element._checkboxElement.checked);
+                this._saveBreakpoints();
+            } else
+                element.removeStyleClass("hidden");
+        }
+
+        WebInspector.startEditing(inputElement, new WebInspector.EditingConfig(finishEditing.bind(this, true), finishEditing.bind(this, false)));
+    },
+
+    highlightBreakpoint: function(url)
+    {
+        var element = this._breakpointElements[url];
+        if (!element)
+            return;
+        this.expand();
+        element.addStyleClass("breakpoint-hit");
+        this._highlightedElement = element;
+    },
+
+    clearBreakpointHighlight: function()
+    {
+        if (this._highlightedElement) {
+            this._highlightedElement.removeStyleClass("breakpoint-hit");
+            delete this._highlightedElement;
+        }
+    },
+
+    _saveBreakpoints: function()
+    {
+        var breakpoints = [];
+        for (var url in this._breakpointElements)
+            breakpoints.push({ url: url, enabled: this._breakpointElements[url]._checkboxElement.checked });
+        WebInspector.settings.xhrBreakpoints.set(breakpoints);
+    },
+
+    _restoreBreakpoints: function()
+    {
+        var breakpoints = WebInspector.settings.xhrBreakpoints.get();
+        for (var i = 0; i < breakpoints.length; ++i) {
+            var breakpoint = breakpoints[i];
+            if (breakpoint && typeof breakpoint.url === "string")
+                this._setBreakpoint(breakpoint.url, breakpoint.enabled);
+        }
+    },
+
+    __proto__: WebInspector.NativeBreakpointsSidebarPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.EventListenerBreakpointsSidebarPane = function()
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Event Listener Breakpoints"));
+    this.registerRequiredCSS("breakpointsList.css");
+
+    this.categoriesElement = document.createElement("ol");
+    this.categoriesElement.tabIndex = 0;
+    this.categoriesElement.addStyleClass("properties-tree");
+    this.categoriesElement.addStyleClass("event-listener-breakpoints");
+    this.categoriesTreeOutline = new TreeOutline(this.categoriesElement);
+    this.bodyElement.appendChild(this.categoriesElement);
+
+    this._breakpointItems = {};
+    // FIXME: uncomment following once inspector stops being drop targer in major ports.
+    // Otherwise, inspector page reacts on drop event and tries to load the event data.
+    // this._createCategory(WebInspector.UIString("Drag"), true, ["drag", "drop", "dragstart", "dragend", "dragenter", "dragleave", "dragover"]);
+    this._createCategory(WebInspector.UIString("Animation"), false, ["requestAnimationFrame", "cancelAnimationFrame", "animationFrameFired"]);
+    this._createCategory(WebInspector.UIString("Control"), true, ["resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", "reset"]);
+    this._createCategory(WebInspector.UIString("Clipboard"), true, ["copy", "cut", "paste", "beforecopy", "beforecut", "beforepaste"]);
+    this._createCategory(WebInspector.UIString("DOM Mutation"), true, ["DOMActivate", "DOMFocusIn", "DOMFocusOut", "DOMAttrModified", "DOMCharacterDataModified", "DOMNodeInserted", "DOMNodeInsertedIntoDocument", "DOMNodeRemoved", "DOMNodeRemovedFromDocument", "DOMSubtreeModified", "DOMContentLoaded"]);
+    this._createCategory(WebInspector.UIString("Device"), true, ["deviceorientation", "devicemotion"]);
+    this._createCategory(WebInspector.UIString("Keyboard"), true, ["keydown", "keyup", "keypress", "input"]);
+    this._createCategory(WebInspector.UIString("Load"), true, ["load", "unload", "abort", "error"]);
+    this._createCategory(WebInspector.UIString("Mouse"), true, ["click", "dblclick", "mousedown", "mouseup", "mouseover", "mousemove", "mouseout", "mousewheel"]);
+    this._createCategory(WebInspector.UIString("Timer"), false, ["setTimer", "clearTimer", "timerFired"]);
+    this._createCategory(WebInspector.UIString("Touch"), true, ["touchstart", "touchmove", "touchend", "touchcancel"]);
+
+    this._restoreBreakpoints();
+}
+
+WebInspector.EventListenerBreakpointsSidebarPane.categotyListener = "listener:";
+WebInspector.EventListenerBreakpointsSidebarPane.categotyInstrumentation = "instrumentation:";
+
+WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI = function(eventName)
+{
+    if (!WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI) {
+        WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI = {
+            "instrumentation:setTimer": WebInspector.UIString("Set Timer"),
+            "instrumentation:clearTimer": WebInspector.UIString("Clear Timer"),
+            "instrumentation:timerFired": WebInspector.UIString("Timer Fired"),
+            "instrumentation:requestAnimationFrame": WebInspector.UIString("Request Animation Frame"),
+            "instrumentation:cancelAnimationFrame": WebInspector.UIString("Cancel Animation Frame"),
+            "instrumentation:animationFrameFired": WebInspector.UIString("Animation Frame Fired")
+        };
+    }
+    return WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI[eventName] || eventName.substring(eventName.indexOf(":") + 1);
+}
+
+WebInspector.EventListenerBreakpointsSidebarPane.prototype = {
+    _createCategory: function(name, isDOMEvent, eventNames)
+    {
+        var categoryItem = {};
+        categoryItem.element = new TreeElement(name);
+        this.categoriesTreeOutline.appendChild(categoryItem.element);
+        categoryItem.element.listItemElement.addStyleClass("event-category");
+        categoryItem.element.selectable = true;
+
+        categoryItem.checkbox = this._createCheckbox(categoryItem.element);
+        categoryItem.checkbox.addEventListener("click", this._categoryCheckboxClicked.bind(this, categoryItem), true);
+
+        categoryItem.children = {};
+        for (var i = 0; i < eventNames.length; ++i) {
+            var eventName = (isDOMEvent ? WebInspector.EventListenerBreakpointsSidebarPane.categotyListener :  WebInspector.EventListenerBreakpointsSidebarPane.categotyInstrumentation) + eventNames[i];
+
+            var breakpointItem = {};
+            var title = WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI(eventName);
+            breakpointItem.element = new TreeElement(title);
+            categoryItem.element.appendChild(breakpointItem.element);
+            var hitMarker = document.createElement("div");
+            hitMarker.className = "breakpoint-hit-marker";
+            breakpointItem.element.listItemElement.appendChild(hitMarker);
+            breakpointItem.element.listItemElement.addStyleClass("source-code");
+            breakpointItem.element.selectable = true;
+
+            breakpointItem.checkbox = this._createCheckbox(breakpointItem.element);
+            breakpointItem.checkbox.addEventListener("click", this._breakpointCheckboxClicked.bind(this, eventName), true);
+            breakpointItem.parent = categoryItem;
+
+            this._breakpointItems[eventName] = breakpointItem;
+            categoryItem.children[eventName] = breakpointItem;
+        }
+    },
+
+    _createCheckbox: function(treeElement)
+    {
+        var checkbox = document.createElement("input");
+        checkbox.className = "checkbox-elem";
+        checkbox.type = "checkbox";
+        treeElement.listItemElement.insertBefore(checkbox, treeElement.listItemElement.firstChild);
+        return checkbox;
+    },
+
+    _categoryCheckboxClicked: function(categoryItem)
+    {
+        var checked = categoryItem.checkbox.checked;
+        for (var eventName in categoryItem.children) {
+            var breakpointItem = categoryItem.children[eventName];
+            if (breakpointItem.checkbox.checked === checked)
+                continue;
+            if (checked)
+                this._setBreakpoint(eventName);
+            else
+                this._removeBreakpoint(eventName);
+        }
+        this._saveBreakpoints();
+    },
+
+    _breakpointCheckboxClicked: function(eventName, event)
+    {
+        if (event.target.checked)
+            this._setBreakpoint(eventName);
+        else
+            this._removeBreakpoint(eventName);
+        this._saveBreakpoints();
+    },
+
+    _setBreakpoint: function(eventName)
+    {
+        var breakpointItem = this._breakpointItems[eventName];
+        if (!breakpointItem)
+            return;
+        breakpointItem.checkbox.checked = true;
+        if (eventName.startsWith(WebInspector.EventListenerBreakpointsSidebarPane.categotyListener))
+            DOMDebuggerAgent.setEventListenerBreakpoint(eventName.substring(WebInspector.EventListenerBreakpointsSidebarPane.categotyListener.length));
+        else if (eventName.startsWith(WebInspector.EventListenerBreakpointsSidebarPane.categotyInstrumentation))
+            DOMDebuggerAgent.setInstrumentationBreakpoint(eventName.substring(WebInspector.EventListenerBreakpointsSidebarPane.categotyInstrumentation.length));
+        this._updateCategoryCheckbox(breakpointItem.parent);
+    },
+
+    _removeBreakpoint: function(eventName)
+    {
+        var breakpointItem = this._breakpointItems[eventName];
+        if (!breakpointItem)
+            return;
+        breakpointItem.checkbox.checked = false;
+        if (eventName.startsWith(WebInspector.EventListenerBreakpointsSidebarPane.categotyListener))
+            DOMDebuggerAgent.removeEventListenerBreakpoint(eventName.substring(WebInspector.EventListenerBreakpointsSidebarPane.categotyListener.length));
+        else if (eventName.startsWith(WebInspector.EventListenerBreakpointsSidebarPane.categotyInstrumentation))
+            DOMDebuggerAgent.removeInstrumentationBreakpoint(eventName.substring(WebInspector.EventListenerBreakpointsSidebarPane.categotyInstrumentation.length));
+        this._updateCategoryCheckbox(breakpointItem.parent);
+    },
+
+    _updateCategoryCheckbox: function(categoryItem)
+    {
+        var hasEnabled = false, hasDisabled = false;
+        for (var eventName in categoryItem.children) {
+            var breakpointItem = categoryItem.children[eventName];
+            if (breakpointItem.checkbox.checked)
+                hasEnabled = true;
+            else
+                hasDisabled = true;
+        }
+        categoryItem.checkbox.checked = hasEnabled;
+        categoryItem.checkbox.indeterminate = hasEnabled && hasDisabled;
+    },
+
+    highlightBreakpoint: function(eventName)
+    {
+        var breakpointItem = this._breakpointItems[eventName];
+        if (!breakpointItem)
+            return;
+        this.expand();
+        breakpointItem.parent.element.expand();
+        breakpointItem.element.listItemElement.addStyleClass("breakpoint-hit");
+        this._highlightedElement = breakpointItem.element.listItemElement;
+    },
+
+    clearBreakpointHighlight: function()
+    {
+        if (this._highlightedElement) {
+            this._highlightedElement.removeStyleClass("breakpoint-hit");
+            delete this._highlightedElement;
+        }
+    },
+
+    _saveBreakpoints: function()
+    {
+        var breakpoints = [];
+        for (var eventName in this._breakpointItems) {
+            if (this._breakpointItems[eventName].checkbox.checked)
+                breakpoints.push({ eventName: eventName });
+        }
+        WebInspector.settings.eventListenerBreakpoints.set(breakpoints);
+    },
+
+    _restoreBreakpoints: function()
+    {
+        var breakpoints = WebInspector.settings.eventListenerBreakpoints.get();
+        for (var i = 0; i < breakpoints.length; ++i) {
+            var breakpoint = breakpoints[i];
+            if (breakpoint && typeof breakpoint.eventName === "string")
+                this._setBreakpoint(breakpoint.eventName);
+        }
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
diff --git a/Source/devtools/front_end/CPUProfileView.js b/Source/devtools/front_end/CPUProfileView.js
new file mode 100644
index 0000000..2839eb2
--- /dev/null
+++ b/Source/devtools/front_end/CPUProfileView.js
@@ -0,0 +1,961 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.CPUProfileHeader} profileHeader
+ */
+WebInspector.CPUProfileView = function(profileHeader)
+{
+    WebInspector.View.call(this);
+
+    this.element.addStyleClass("profile-view");
+    
+    this.showSelfTimeAsPercent = WebInspector.settings.createSetting("cpuProfilerShowSelfTimeAsPercent", true);
+    this.showTotalTimeAsPercent = WebInspector.settings.createSetting("cpuProfilerShowTotalTimeAsPercent", true);
+    this.showAverageTimeAsPercent = WebInspector.settings.createSetting("cpuProfilerShowAverageTimeAsPercent", true);
+    this._viewType = WebInspector.settings.createSetting("cpuProfilerView", WebInspector.CPUProfileView._TypeHeavy);
+
+    var columns = [];
+    columns.push({id: "self", title: WebInspector.UIString("Self"), width: "72px", sort: WebInspector.DataGrid.Order.Descending, sortable: true});
+    columns.push({id: "total", title: WebInspector.UIString("Total"), width: "72px", sortable: true});
+    columns.push({id: "function", title: WebInspector.UIString("Function"), disclosure: true, sortable: true});
+
+    this.dataGrid = new WebInspector.DataGrid(columns);
+    this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortProfile, this);
+    this.dataGrid.element.addEventListener("mousedown", this._mouseDownInDataGrid.bind(this), true);
+    this.dataGrid.show(this.element);
+
+    this.viewSelectComboBox = new WebInspector.StatusBarComboBox(this._changeView.bind(this));
+
+    var options = {};
+    options[WebInspector.CPUProfileView._TypeFlame] = this.viewSelectComboBox.createOption(WebInspector.UIString("Flame Chart"), "", WebInspector.CPUProfileView._TypeFlame);
+    options[WebInspector.CPUProfileView._TypeHeavy] = this.viewSelectComboBox.createOption(WebInspector.UIString("Heavy (Bottom Up)"), "", WebInspector.CPUProfileView._TypeHeavy);
+    options[WebInspector.CPUProfileView._TypeTree] = this.viewSelectComboBox.createOption(WebInspector.UIString("Tree (Top Down)"), "", WebInspector.CPUProfileView._TypeTree);
+
+    var optionName = this._viewType.get() || WebInspector.CPUProfileView._TypeFlame;
+    var option = options[optionName] || options[WebInspector.CPUProfileView._TypeFlame];
+    this.viewSelectComboBox.select(option);
+
+    this._statusBarButtonsElement = document.createElement("span");
+
+    this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item");
+    this.percentButton.addEventListener("click", this._percentClicked, this);
+    this._statusBarButtonsElement.appendChild(this.percentButton.element);
+
+    this.focusButton = new WebInspector.StatusBarButton(WebInspector.UIString("Focus selected function."), "focus-profile-node-status-bar-item");
+    this.focusButton.setEnabled(false);
+    this.focusButton.addEventListener("click", this._focusClicked, this);
+    this._statusBarButtonsElement.appendChild(this.focusButton.element);
+
+    this.excludeButton = new WebInspector.StatusBarButton(WebInspector.UIString("Exclude selected function."), "exclude-profile-node-status-bar-item");
+    this.excludeButton.setEnabled(false);
+    this.excludeButton.addEventListener("click", this._excludeClicked, this);
+    this._statusBarButtonsElement.appendChild(this.excludeButton.element);
+
+    this.resetButton = new WebInspector.StatusBarButton(WebInspector.UIString("Restore all functions."), "reset-profile-status-bar-item");
+    this.resetButton.visible = false;
+    this.resetButton.addEventListener("click", this._resetClicked, this);
+    this._statusBarButtonsElement.appendChild(this.resetButton.element);
+
+    this.profileHead = /** @type {?ProfilerAgent.CPUProfileNode} */ (null);
+    this.profileHeader = profileHeader;
+
+    this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultFormatter(30));
+
+    if (this.profileHeader._profile) // If the profile has been loaded from file then use it.
+        this._processProfileData(this.profileHeader._profile);
+    else
+        ProfilerAgent.getCPUProfile(this.profileHeader.uid, this._getCPUProfileCallback.bind(this));
+}
+
+WebInspector.CPUProfileView._TypeFlame = "Flame";
+WebInspector.CPUProfileView._TypeTree = "Tree";
+WebInspector.CPUProfileView._TypeHeavy = "Heavy";
+
+WebInspector.CPUProfileView.prototype = {
+    /**
+     * @param {!number} timeLeft
+     * @param {!number} timeRight
+     */
+    selectRange: function(timeLeft, timeRight)
+    {
+        if (!this._flameChart)
+            return;
+        this._flameChart.selectRange(timeLeft, timeRight);
+    },
+
+    _revealProfilerNode: function(event)
+    {
+        var current = this.profileDataGridTree.children[0];
+
+        while (current && current.profileNode !== event.data)
+            current = current.traverseNextNode(false, null, false);
+
+        if (current)
+            current.revealAndSelect();
+    },
+
+    /**
+     * @param {?Protocol.Error} error
+     * @param {ProfilerAgent.CPUProfile} profile
+     */
+    _getCPUProfileCallback: function(error, profile)
+    {
+        if (error)
+            return;
+
+        if (!profile.head) {
+            // Profiling was tentatively terminated with the "Clear all profiles." button.
+            return;
+        }
+
+        this._processProfileData(profile);
+    },
+
+    _processProfileData: function(profile)
+    {
+        this.profileHead = profile.head;
+        this.samples = profile.samples;
+
+        if (profile.idleTime)
+            this._injectIdleTimeNode(profile);
+
+        this._assignParentsInProfile();
+        if (this.samples)
+            this._buildIdToNodeMap();
+        this._changeView();
+        this._updatePercentButton();
+        if (this._flameChart)
+            this._flameChart.update();
+    },
+
+    get statusBarItems()
+    {
+        return [this.viewSelectComboBox.element, this._statusBarButtonsElement];
+    },
+
+    /**
+     * @return {!WebInspector.ProfileDataGridTree}
+     */
+    _getBottomUpProfileDataGridTree: function()
+    {
+        if (!this._bottomUpProfileDataGridTree)
+            this._bottomUpProfileDataGridTree = new WebInspector.BottomUpProfileDataGridTree(this, this.profileHead);
+        return this._bottomUpProfileDataGridTree;
+    },
+
+    /**
+     * @return {!WebInspector.ProfileDataGridTree}
+     */
+    _getTopDownProfileDataGridTree: function()
+    {
+        if (!this._topDownProfileDataGridTree)
+            this._topDownProfileDataGridTree = new WebInspector.TopDownProfileDataGridTree(this, this.profileHead);
+        return this._topDownProfileDataGridTree;
+    },
+
+    willHide: function()
+    {
+        this._currentSearchResultIndex = -1;
+    },
+
+    refresh: function()
+    {
+        var selectedProfileNode = this.dataGrid.selectedNode ? this.dataGrid.selectedNode.profileNode : null;
+
+        this.dataGrid.rootNode().removeChildren();
+
+        var children = this.profileDataGridTree.children;
+        var count = children.length;
+
+        for (var index = 0; index < count; ++index)
+            this.dataGrid.rootNode().appendChild(children[index]);
+
+        if (selectedProfileNode)
+            selectedProfileNode.selected = true;
+    },
+
+    refreshVisibleData: function()
+    {
+        var child = this.dataGrid.rootNode().children[0];
+        while (child) {
+            child.refresh();
+            child = child.traverseNextNode(false, null, true);
+        }
+    },
+
+    refreshShowAsPercents: function()
+    {
+        this._updatePercentButton();
+        this.refreshVisibleData();
+    },
+
+    searchCanceled: function()
+    {
+        if (this._searchResults) {
+            for (var i = 0; i < this._searchResults.length; ++i) {
+                var profileNode = this._searchResults[i].profileNode;
+
+                delete profileNode._searchMatchedSelfColumn;
+                delete profileNode._searchMatchedTotalColumn;
+                delete profileNode._searchMatchedAverageColumn;
+                delete profileNode._searchMatchedCallsColumn;
+                delete profileNode._searchMatchedFunctionColumn;
+
+                profileNode.refresh();
+            }
+        }
+
+        delete this._searchFinishedCallback;
+        this._currentSearchResultIndex = -1;
+        this._searchResults = [];
+    },
+
+    performSearch: function(query, finishedCallback)
+    {
+        // Call searchCanceled since it will reset everything we need before doing a new search.
+        this.searchCanceled();
+
+        query = query.trim();
+
+        if (!query.length)
+            return;
+
+        this._searchFinishedCallback = finishedCallback;
+
+        var greaterThan = (query.startsWith(">"));
+        var lessThan = (query.startsWith("<"));
+        var equalTo = (query.startsWith("=") || ((greaterThan || lessThan) && query.indexOf("=") === 1));
+        var percentUnits = (query.lastIndexOf("%") === (query.length - 1));
+        var millisecondsUnits = (query.length > 2 && query.lastIndexOf("ms") === (query.length - 2));
+        var secondsUnits = (!millisecondsUnits && query.lastIndexOf("s") === (query.length - 1));
+
+        var queryNumber = parseFloat(query);
+        if (greaterThan || lessThan || equalTo) {
+            if (equalTo && (greaterThan || lessThan))
+                queryNumber = parseFloat(query.substring(2));
+            else
+                queryNumber = parseFloat(query.substring(1));
+        }
+
+        var queryNumberMilliseconds = (secondsUnits ? (queryNumber * 1000) : queryNumber);
+
+        // Make equalTo implicitly true if it wasn't specified there is no other operator.
+        if (!isNaN(queryNumber) && !(greaterThan || lessThan))
+            equalTo = true;
+
+        var matcher = new RegExp(query.escapeForRegExp(), "i");
+
+        function matchesQuery(/*ProfileDataGridNode*/ profileDataGridNode)
+        {
+            delete profileDataGridNode._searchMatchedSelfColumn;
+            delete profileDataGridNode._searchMatchedTotalColumn;
+            delete profileDataGridNode._searchMatchedAverageColumn;
+            delete profileDataGridNode._searchMatchedCallsColumn;
+            delete profileDataGridNode._searchMatchedFunctionColumn;
+
+            if (percentUnits) {
+                if (lessThan) {
+                    if (profileDataGridNode.selfPercent < queryNumber)
+                        profileDataGridNode._searchMatchedSelfColumn = true;
+                    if (profileDataGridNode.totalPercent < queryNumber)
+                        profileDataGridNode._searchMatchedTotalColumn = true;
+                    if (profileDataGridNode.averagePercent < queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedAverageColumn = true;
+                } else if (greaterThan) {
+                    if (profileDataGridNode.selfPercent > queryNumber)
+                        profileDataGridNode._searchMatchedSelfColumn = true;
+                    if (profileDataGridNode.totalPercent > queryNumber)
+                        profileDataGridNode._searchMatchedTotalColumn = true;
+                    if (profileDataGridNode.averagePercent < queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedAverageColumn = true;
+                }
+
+                if (equalTo) {
+                    if (profileDataGridNode.selfPercent == queryNumber)
+                        profileDataGridNode._searchMatchedSelfColumn = true;
+                    if (profileDataGridNode.totalPercent == queryNumber)
+                        profileDataGridNode._searchMatchedTotalColumn = true;
+                    if (profileDataGridNode.averagePercent < queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedAverageColumn = true;
+                }
+            } else if (millisecondsUnits || secondsUnits) {
+                if (lessThan) {
+                    if (profileDataGridNode.selfTime < queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedSelfColumn = true;
+                    if (profileDataGridNode.totalTime < queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedTotalColumn = true;
+                    if (profileDataGridNode.averageTime < queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedAverageColumn = true;
+                } else if (greaterThan) {
+                    if (profileDataGridNode.selfTime > queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedSelfColumn = true;
+                    if (profileDataGridNode.totalTime > queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedTotalColumn = true;
+                    if (profileDataGridNode.averageTime > queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedAverageColumn = true;
+                }
+
+                if (equalTo) {
+                    if (profileDataGridNode.selfTime == queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedSelfColumn = true;
+                    if (profileDataGridNode.totalTime == queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedTotalColumn = true;
+                    if (profileDataGridNode.averageTime == queryNumberMilliseconds)
+                        profileDataGridNode._searchMatchedAverageColumn = true;
+                }
+            } else {
+                if (equalTo && profileDataGridNode.numberOfCalls == queryNumber)
+                    profileDataGridNode._searchMatchedCallsColumn = true;
+                if (greaterThan && profileDataGridNode.numberOfCalls > queryNumber)
+                    profileDataGridNode._searchMatchedCallsColumn = true;
+                if (lessThan && profileDataGridNode.numberOfCalls < queryNumber)
+                    profileDataGridNode._searchMatchedCallsColumn = true;
+            }
+
+            if (profileDataGridNode.functionName.match(matcher) || (profileDataGridNode.url && profileDataGridNode.url.match(matcher)))
+                profileDataGridNode._searchMatchedFunctionColumn = true;
+
+            if (profileDataGridNode._searchMatchedSelfColumn ||
+                profileDataGridNode._searchMatchedTotalColumn ||
+                profileDataGridNode._searchMatchedAverageColumn ||
+                profileDataGridNode._searchMatchedCallsColumn ||
+                profileDataGridNode._searchMatchedFunctionColumn)
+            {
+                profileDataGridNode.refresh();
+                return true;
+            }
+
+            return false;
+        }
+
+        var current = this.profileDataGridTree.children[0];
+
+        while (current) {
+            if (matchesQuery(current)) {
+                this._searchResults.push({ profileNode: current });
+            }
+
+            current = current.traverseNextNode(false, null, false);
+        }
+
+        finishedCallback(this, this._searchResults.length);
+    },
+
+    jumpToFirstSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        this._currentSearchResultIndex = 0;
+        this._jumpToSearchResult(this._currentSearchResultIndex);
+    },
+
+    jumpToLastSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        this._currentSearchResultIndex = (this._searchResults.length - 1);
+        this._jumpToSearchResult(this._currentSearchResultIndex);
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        if (++this._currentSearchResultIndex >= this._searchResults.length)
+            this._currentSearchResultIndex = 0;
+        this._jumpToSearchResult(this._currentSearchResultIndex);
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        if (--this._currentSearchResultIndex < 0)
+            this._currentSearchResultIndex = (this._searchResults.length - 1);
+        this._jumpToSearchResult(this._currentSearchResultIndex);
+    },
+
+    showingFirstSearchResult: function()
+    {
+        return (this._currentSearchResultIndex === 0);
+    },
+
+    showingLastSearchResult: function()
+    {
+        return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
+    },
+
+    _jumpToSearchResult: function(index)
+    {
+        var searchResult = this._searchResults[index];
+        if (!searchResult)
+            return;
+
+        var profileNode = searchResult.profileNode;
+        profileNode.revealAndSelect();
+    },
+
+    _ensureFlameChartCreated: function()
+    {
+        if (this._flameChart)
+            return;
+        this._flameChart = new WebInspector.FlameChart(this);
+        this._flameChart.addEventListener(WebInspector.FlameChart.Events.SelectedNode, this._onSelectedNode.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onSelectedNode: function(event)
+    {
+        var node = event.data;
+        if (!node || !node.url)
+            return;
+        var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(node.url);
+        if (!uiSourceCode)
+            return;
+        WebInspector.showPanel("scripts").showUISourceCode(uiSourceCode, node.lineNumber);
+    },
+
+    _changeView: function()
+    {
+        if (!this.profileHeader)
+            return;
+
+        switch (this.viewSelectComboBox.selectedOption().value) {
+        case WebInspector.CPUProfileView._TypeFlame:
+            this._ensureFlameChartCreated();
+            this.dataGrid.detach();
+            this._flameChart.show(this.element);
+            this._viewType.set(WebInspector.CPUProfileView._TypeFlame);
+            this._statusBarButtonsElement.enableStyleClass("hidden", true);
+            return;
+        case WebInspector.CPUProfileView._TypeTree:
+            this.profileDataGridTree = this._getTopDownProfileDataGridTree();
+            this._sortProfile();
+            this._viewType.set(WebInspector.CPUProfileView._TypeTree);
+            break;
+        case WebInspector.CPUProfileView._TypeHeavy:
+            this.profileDataGridTree = this._getBottomUpProfileDataGridTree();
+            this._sortProfile();
+            this._viewType.set(WebInspector.CPUProfileView._TypeHeavy);
+            break;
+        }
+
+        this._statusBarButtonsElement.enableStyleClass("hidden", false);
+
+        if (this._flameChart)
+            this._flameChart.detach();
+        this.dataGrid.show(this.element);
+
+        if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
+            return;
+
+        // The current search needs to be performed again. First negate out previous match
+        // count by calling the search finished callback with a negative number of matches.
+        // Then perform the search again the with same query and callback.
+        this._searchFinishedCallback(this, -this._searchResults.length);
+        this.performSearch(this.currentQuery, this._searchFinishedCallback);
+    },
+
+    _percentClicked: function(event)
+    {
+        var currentState = this.showSelfTimeAsPercent.get() && this.showTotalTimeAsPercent.get() && this.showAverageTimeAsPercent.get();
+        this.showSelfTimeAsPercent.set(!currentState);
+        this.showTotalTimeAsPercent.set(!currentState);
+        this.showAverageTimeAsPercent.set(!currentState);
+        this.refreshShowAsPercents();
+    },
+
+    _updatePercentButton: function()
+    {
+        if (this.showSelfTimeAsPercent.get() && this.showTotalTimeAsPercent.get() && this.showAverageTimeAsPercent.get()) {
+            this.percentButton.title = WebInspector.UIString("Show absolute total and self times.");
+            this.percentButton.toggled = true;
+        } else {
+            this.percentButton.title = WebInspector.UIString("Show total and self times as percentages.");
+            this.percentButton.toggled = false;
+        }
+    },
+
+    _focusClicked: function(event)
+    {
+        if (!this.dataGrid.selectedNode)
+            return;
+
+        this.resetButton.visible = true;
+        this.profileDataGridTree.focus(this.dataGrid.selectedNode);
+        this.refresh();
+        this.refreshVisibleData();
+    },
+
+    _excludeClicked: function(event)
+    {
+        var selectedNode = this.dataGrid.selectedNode
+
+        if (!selectedNode)
+            return;
+
+        selectedNode.deselect();
+
+        this.resetButton.visible = true;
+        this.profileDataGridTree.exclude(selectedNode);
+        this.refresh();
+        this.refreshVisibleData();
+    },
+
+    _resetClicked: function(event)
+    {
+        this.resetButton.visible = false;
+        this.profileDataGridTree.restore();
+        this._linkifier.reset();
+        this.refresh();
+        this.refreshVisibleData();
+    },
+
+    _dataGridNodeSelected: function(node)
+    {
+        this.focusButton.setEnabled(true);
+        this.excludeButton.setEnabled(true);
+    },
+
+    _dataGridNodeDeselected: function(node)
+    {
+        this.focusButton.setEnabled(false);
+        this.excludeButton.setEnabled(false);
+    },
+
+    _sortProfile: function()
+    {
+        var sortAscending = this.dataGrid.isSortOrderAscending();
+        var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier();
+        var sortProperty = {
+                "average": "averageTime",
+                "self": "selfTime",
+                "total": "totalTime",
+                "calls": "numberOfCalls",
+                "function": "functionName"
+            }[sortColumnIdentifier];
+
+        this.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyComparator(sortProperty, sortAscending));
+
+        this.refresh();
+    },
+
+    _mouseDownInDataGrid: function(event)
+    {
+        if (event.detail < 2)
+            return;
+
+        var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
+        if (!cell || (!cell.hasStyleClass("total-column") && !cell.hasStyleClass("self-column") && !cell.hasStyleClass("average-column")))
+            return;
+
+        if (cell.hasStyleClass("total-column"))
+            this.showTotalTimeAsPercent.set(!this.showTotalTimeAsPercent.get());
+        else if (cell.hasStyleClass("self-column"))
+            this.showSelfTimeAsPercent.set(!this.showSelfTimeAsPercent.get());
+        else if (cell.hasStyleClass("average-column"))
+            this.showAverageTimeAsPercent.set(!this.showAverageTimeAsPercent.get());
+
+        this.refreshShowAsPercents();
+
+        event.consume(true);
+    },
+
+    _assignParentsInProfile: function()
+    {
+        var head = this.profileHead;
+        head.parent = null;
+        head.head = null;
+        var nodesToTraverse = [ { parent: head, children: head.children } ];
+        while (nodesToTraverse.length > 0) {
+            var pair = nodesToTraverse.pop();
+            var parent = pair.parent;
+            var children = pair.children;
+            var length = children.length;
+            for (var i = 0; i < length; ++i) {
+                children[i].head = head;
+                children[i].parent = parent;
+                if (children[i].children.length > 0)
+                    nodesToTraverse.push({ parent: children[i], children: children[i].children });
+            }
+        }
+    },
+
+    _buildIdToNodeMap: function()
+    {
+        var idToNode = this._idToNode = {};
+        var stack = [this.profileHead];
+        while (stack.length) {
+            var node = stack.pop();
+            idToNode[node.id] = node;
+            for (var i = 0; i < node.children.length; i++)
+                stack.push(node.children[i]);
+        }
+    },
+
+    /**
+     * @param {ProfilerAgent.CPUProfile} profile
+     */
+    _injectIdleTimeNode: function(profile)
+    {
+        var idleTime = profile.idleTime;
+        var nodes = profile.head.children;
+
+        var programNode = {selfTime: 0};
+        for (var i = nodes.length - 1; i >= 0; --i) {
+            if (nodes[i].functionName === "(program)") {
+                programNode = nodes[i];
+                break;
+            }
+        }
+        var programTime = programNode.selfTime;
+        if (idleTime > programTime)
+            idleTime = programTime;
+        programTime = programTime - idleTime;
+        programNode.selfTime = programTime;
+        programNode.totalTime = programTime;
+        var idleNode = {
+            functionName: "(idle)",
+            url: null,
+            lineNumber: 0,
+            totalTime: idleTime,
+            selfTime: idleTime,
+            numberOfCalls: 0,
+            visible: true,
+            callUID: 0,
+            children: []
+        };
+        nodes.push(idleNode);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileType}
+ * @implements {ProfilerAgent.Dispatcher}
+ */
+WebInspector.CPUProfileType = function()
+{
+    WebInspector.ProfileType.call(this, WebInspector.CPUProfileType.TypeId, WebInspector.UIString("Collect JavaScript CPU Profile"));
+    InspectorBackend.registerProfilerDispatcher(this);
+    this._recording = false;
+    WebInspector.CPUProfileType.instance = this;
+}
+
+WebInspector.CPUProfileType.TypeId = "CPU";
+
+WebInspector.CPUProfileType.prototype = {
+    /**
+     * @override
+     * @return {string}
+     */
+    fileExtension: function()
+    {
+        return ".cpuprofile";
+    },
+
+    get buttonTooltip()
+    {
+        return this._recording ? WebInspector.UIString("Stop CPU profiling.") : WebInspector.UIString("Start CPU profiling.");
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    buttonClicked: function()
+    {
+        if (this._recording) {
+            this.stopRecordingProfile();
+            return false;
+        } else {
+            this.startRecordingProfile();
+            return true;
+        }
+    },
+
+    get treeItemTitle()
+    {
+        return WebInspector.UIString("CPU PROFILES");
+    },
+
+    get description()
+    {
+        return WebInspector.UIString("CPU profiles show where the execution time is spent in your page's JavaScript functions.");
+    },
+
+    /**
+     * @param {ProfilerAgent.ProfileHeader} profileHeader
+     */
+    addProfileHeader: function(profileHeader)
+    {
+        this.addProfile(this.createProfile(profileHeader));
+    },
+
+    isRecordingProfile: function()
+    {
+        return this._recording;
+    },
+
+    startRecordingProfile: function()
+    {
+        this._recording = true;
+        WebInspector.userMetrics.ProfilesCPUProfileTaken.record();
+        ProfilerAgent.start();
+    },
+
+    stopRecordingProfile: function()
+    {
+        this._recording = false;
+        ProfilerAgent.stop();
+    },
+
+    /**
+     * @param {boolean} isProfiling
+     */
+    setRecordingProfile: function(isProfiling)
+    {
+        this._recording = isProfiling;
+    },
+
+    /**
+     * @override
+     * @param {string=} title
+     * @return {!WebInspector.ProfileHeader}
+     */
+    createTemporaryProfile: function(title)
+    {
+        title = title || WebInspector.UIString("Recording\u2026");
+        return new WebInspector.CPUProfileHeader(this, title);
+    },
+
+    /**
+     * @override
+     * @param {ProfilerAgent.ProfileHeader} profile
+     * @return {!WebInspector.ProfileHeader}
+     */
+    createProfile: function(profile)
+    {
+        return new WebInspector.CPUProfileHeader(this, profile.title, profile.uid);
+    },
+
+    /**
+     * @override
+     * @param {!WebInspector.ProfileHeader} profile
+     */
+    removeProfile: function(profile)
+    {
+        WebInspector.ProfileType.prototype.removeProfile.call(this, profile);
+        if (!profile.isTemporary)
+            ProfilerAgent.removeProfile(this.id, profile.uid);
+    },
+
+    /**
+     * @override
+     * @param {function(this:WebInspector.ProfileType, ?string, Array.<ProfilerAgent.ProfileHeader>)} populateCallback
+     */
+    _requestProfilesFromBackend: function(populateCallback)
+    {
+        ProfilerAgent.getProfileHeaders(populateCallback);
+    },
+
+    /**
+     * @override
+     */
+    resetProfiles: function()
+    {
+        this._reset();
+    },
+
+    /** @deprecated To be removed from the protocol */
+    addHeapSnapshotChunk: function(uid, chunk)
+    {
+        throw new Error("Never called");
+    },
+
+    /** @deprecated To be removed from the protocol */
+    finishHeapSnapshot: function(uid)
+    {
+        throw new Error("Never called");
+    },
+
+    /** @deprecated To be removed from the protocol */
+    reportHeapSnapshotProgress: function(done, total)
+    {
+        throw new Error("Never called");
+    },
+
+    __proto__: WebInspector.ProfileType.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileHeader}
+ * @implements {WebInspector.OutputStream}
+ * @implements {WebInspector.OutputStreamDelegate}
+ * @param {!WebInspector.CPUProfileType} type
+ * @param {string} title
+ * @param {number=} uid
+ */
+WebInspector.CPUProfileHeader = function(type, title, uid)
+{
+    WebInspector.ProfileHeader.call(this, type, title, uid);
+}
+
+WebInspector.CPUProfileHeader.prototype = {
+    onTransferStarted: function()
+    {
+        this._jsonifiedProfile = "";
+        this.sidebarElement.subtitle = WebInspector.UIString("Loading\u2026 %s", Number.bytesToString(this._jsonifiedProfile.length));
+    },
+
+    /**
+     * @param {WebInspector.ChunkedReader} reader
+     */
+    onChunkTransferred: function(reader)
+    {
+        this.sidebarElement.subtitle = WebInspector.UIString("Loading\u2026 %d\%", Number.bytesToString(this._jsonifiedProfile.length));
+    },
+
+    onTransferFinished: function()
+    {
+
+        this.sidebarElement.subtitle = WebInspector.UIString("Parsing\u2026");
+        this._profile = JSON.parse(this._jsonifiedProfile);
+        this._jsonifiedProfile = null;
+        this.sidebarElement.subtitle = WebInspector.UIString("Loaded");
+        this.isTemporary = false;
+    },
+
+    /**
+     * @param {WebInspector.ChunkedReader} reader
+     */
+    onError: function(reader, e)
+    {
+        switch(e.target.error.code) {
+        case e.target.error.NOT_FOUND_ERR:
+            this.sidebarElement.subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
+        break;
+        case e.target.error.NOT_READABLE_ERR:
+            this.sidebarElement.subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
+        break;
+        case e.target.error.ABORT_ERR:
+            break;
+        default:
+            this.sidebarElement.subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
+        }
+    },
+
+    /**
+     * @param {string} text
+     */
+    write: function(text)
+    {
+        this._jsonifiedProfile += text;
+    },
+
+    close: function() { },
+
+    /**
+     * @override
+     */
+    createSidebarTreeElement: function()
+    {
+        return new WebInspector.ProfileSidebarTreeElement(this, WebInspector.UIString("Profile %d"), "profile-sidebar-tree-item");
+    },
+
+    /**
+     * @override
+     * @param {WebInspector.ProfilesPanel} profilesPanel
+     */
+    createView: function(profilesPanel)
+    {
+        return new WebInspector.CPUProfileView(this);
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    canSaveToFile: function()
+    {
+        return true;
+    },
+
+    saveToFile: function()
+    {
+        var fileOutputStream = new WebInspector.FileOutputStream();
+
+        /**
+         * @param {?Protocol.Error} error
+         * @param {ProfilerAgent.CPUProfile} profile
+         */
+        function getCPUProfileCallback(error, profile)
+        {
+            if (error) {
+                fileOutputStream.close();
+                return;
+            }
+
+            if (!profile.head) {
+                // Profiling was tentatively terminated with the "Clear all profiles." button.
+                fileOutputStream.close();
+                return;
+            }
+
+            fileOutputStream.write(JSON.stringify(profile), fileOutputStream.close.bind(fileOutputStream));
+        }
+
+        function onOpen()
+        {
+            ProfilerAgent.getCPUProfile(this.uid, getCPUProfileCallback.bind(this));
+        }
+
+        this._fileName = this._fileName || "CPU-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
+        fileOutputStream.open(this._fileName, onOpen.bind(this));
+    },
+
+    /**
+     * @param {File} file
+     */
+    loadFromFile: function(file)
+    {
+        this.title = file.name;
+        this.sidebarElement.subtitle = WebInspector.UIString("Loading\u2026");
+        this.sidebarElement.wait = true;
+
+        var fileReader = new WebInspector.ChunkedFileReader(file, 10000000, this);
+        fileReader.start(this);
+    },
+
+    __proto__: WebInspector.ProfileHeader.prototype
+}
diff --git a/Source/devtools/front_end/CSSMetadata.js b/Source/devtools/front_end/CSSMetadata.js
new file mode 100644
index 0000000..8c4a09b
--- /dev/null
+++ b/Source/devtools/front_end/CSSMetadata.js
@@ -0,0 +1,962 @@
+/*
+ * Copyright (C) 2010 Nikita Vasilyev. All rights reserved.
+ * Copyright (C) 2010 Joseph Pecoraro. All rights reserved.
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {Array.<CSSAgent.CSSPropertyInfo|string>} properties
+ */
+WebInspector.CSSMetadata = function(properties)
+{
+    this._values = /** !Array.<string> */ ([]);
+    this._longhands = {};
+    this._shorthands = {};
+    for (var i = 0; i < properties.length; ++i) {
+        var property = properties[i];
+        if (typeof property === "string") {
+            this._values.push(property);
+            continue;
+        }
+
+        var propertyName = property.name;
+        this._values.push(propertyName);
+
+        var longhands = properties[i].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();
+}
+
+/**
+ * @type {!WebInspector.CSSMetadata}
+ */
+WebInspector.CSSMetadata.cssPropertiesMetainfo = new WebInspector.CSSMetadata([]);
+
+WebInspector.CSSMetadata.isColorAwareProperty = function(propertyName)
+{
+    return WebInspector.CSSMetadata._colorAwareProperties[propertyName] === true;
+}
+
+WebInspector.CSSMetadata.colors = function()
+{
+    if (!WebInspector.CSSMetadata._colorsKeySet)
+        WebInspector.CSSMetadata._colorsKeySet = WebInspector.CSSMetadata._colors.keySet();
+    return WebInspector.CSSMetadata._colorsKeySet;
+}
+
+// Taken from http://www.w3.org/TR/CSS21/propidx.html.
+WebInspector.CSSMetadata.InheritedProperties = [
+    "azimuth", "border-collapse", "border-spacing", "caption-side", "color", "cursor", "direction", "elevation",
+    "empty-cells", "font-family", "font-size", "font-style", "font-variant", "font-weight", "font", "letter-spacing",
+    "line-height", "list-style-image", "list-style-position", "list-style-type", "list-style", "orphans", "pitch-range",
+    "pitch", "quotes", "resize", "richness", "speak-header", "speak-numeral", "speak-punctuation", "speak", "speech-rate", "stress",
+    "text-align", "text-indent", "text-transform", "text-shadow", "visibility", "voice-family", "volume", "white-space", "widows",
+    "word-spacing", "zoom"
+].keySet();
+
+WebInspector.CSSMetadata._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", "rosybrown",
+    "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna", "skyblue", "slateblue",
+    "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan", "thistle", "tomato", "turquoise", "violet",
+    "wheat", "whitesmoke", "yellowgreen"
+];
+
+WebInspector.CSSMetadata._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-color",
+    "-webkit-text-decoration-color", "-webkit-text-emphasis", "-webkit-text-emphasis-color"
+].keySet();
+
+WebInspector.CSSMetadata._propertyDataMap = {
+    "table-layout": { values: [
+        "auto", "fixed"
+    ] },
+    "visibility": { values: [
+        "hidden", "visible", "collapse"
+    ] },
+    "background-repeat": { values: [
+        "repeat", "repeat-x", "repeat-y", "no-repeat", "space", "round"
+    ] },
+    "text-underline": { values: [
+        "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave"
+    ] },
+    "content": { values: [
+        "list-item", "close-quote", "no-close-quote", "no-open-quote", "open-quote"
+    ] },
+    "list-style-image": { values: [
+        "none"
+    ] },
+    "clear": { values: [
+        "none", "left", "right", "both"
+    ] },
+    "text-underline-mode": { values: [
+        "continuous", "skip-white-space"
+    ] },
+    "overflow-x": { values: [
+        "hidden", "auto", "visible", "overlay", "scroll"
+    ] },
+    "stroke-linejoin": { values: [
+        "round", "miter", "bevel"
+    ] },
+    "baseline-shift": { values: [
+        "baseline", "sub", "super"
+    ] },
+    "border-bottom-width": { values: [
+        "medium", "thick", "thin"
+    ] },
+    "marquee-speed": { values: [
+        "normal", "slow", "fast"
+    ] },
+    "margin-top-collapse": { values: [
+        "collapse", "separate", "discard"
+    ] },
+    "max-height": { values: [
+        "none"
+    ] },
+    "box-orient": { values: [
+        "horizontal", "vertical", "inline-axis", "block-axis"
+    ], },
+    "font-stretch": { values: [
+        "normal", "wider", "narrower", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
+        "semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
+    ] },
+    "-webkit-color-correction": { values: [
+        "default", "srgb"
+    ] },
+    "text-underline-style": { values: [
+        "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave"
+    ] },
+    "text-overline-mode": { values: [
+        "continuous", "skip-white-space"
+    ] },
+    "-webkit-background-composite": { values: [
+        "highlight", "clear", "copy", "source-over", "source-in", "source-out", "source-atop", "destination-over",
+        "destination-in", "destination-out", "destination-atop", "xor", "plus-darker", "plus-lighter"
+    ] },
+    "border-left-width": { values: [
+        "medium", "thick", "thin"
+    ] },
+    "-webkit-writing-mode": { values: [
+        "lr", "rl", "tb", "lr-tb", "rl-tb", "tb-rl", "horizontal-tb", "vertical-rl", "vertical-lr", "horizontal-bt"
+    ] },
+    "text-line-through-mode": { values: [
+        "continuous", "skip-white-space"
+    ] },
+    "border-collapse": { values: [
+        "collapse", "separate"
+    ] },
+    "page-break-inside": { values: [
+        "auto", "avoid"
+    ] },
+    "border-top-width": { values: [
+        "medium", "thick", "thin"
+    ] },
+    "outline-color": { values: [
+        "invert"
+    ] },
+    "text-line-through-style": { values: [
+        "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave"
+    ] },
+    "outline-style": { values: [
+        "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+    ] },
+    "cursor": { values: [
+        "none", "copy", "auto", "crosshair", "default", "pointer", "move", "vertical-text", "cell", "context-menu",
+        "alias", "progress", "no-drop", "not-allowed", "-webkit-zoom-in", "-webkit-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-grabbing"
+    ] },
+    "border-width": { values: [
+        "medium", "thick", "thin"
+    ] },
+    "size": { values: [
+        "a3", "a4", "a5", "b4", "b5", "landscape", "ledger", "legal", "letter", "portrait"
+    ] },
+    "background-size": { values: [
+        "contain", "cover"
+    ] },
+    "direction": { values: [
+        "ltr", "rtl"
+    ] },
+    "marquee-direction": { values: [
+        "left", "right", "auto", "reverse", "forwards", "backwards", "ahead", "up", "down"
+    ] },
+    "enable-background": { values: [
+        "accumulate", "new"
+    ] },
+    "float": { values: [
+        "none", "left", "right"
+    ] },
+    "overflow-y": { values: [
+        "hidden", "auto", "visible", "overlay", "scroll"
+    ] },
+    "margin-bottom-collapse": { values: [
+        "collapse",  "separate", "discard"
+    ] },
+    "box-reflect": { values: [
+        "left", "right", "above", "below"
+    ] },
+    "overflow": { values: [
+        "hidden", "auto", "visible", "overlay", "scroll"
+    ] },
+    "text-rendering": { values: [
+        "auto", "optimizeSpeed", "optimizeLegibility", "geometricPrecision"
+    ] },
+    "text-align": { values: [
+        "-webkit-auto", "start", "end", "left", "right", "center", "justify", "-webkit-left", "-webkit-right", "-webkit-center"
+    ] },
+    "list-style-position": { values: [
+        "outside", "inside", "hanging"
+    ] },
+    "margin-bottom": { values: [
+        "auto"
+    ] },
+    "color-interpolation": { values: [
+        "linearrgb"
+    ] },
+    "background-origin": { values: [
+        "border-box", "content-box", "padding-box"
+    ] },
+    "word-wrap": { values: [
+        "normal", "break-word"
+    ] },
+    "font-weight": { values: [
+        "normal", "bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900"
+    ] },
+    "margin-before-collapse": { values: [
+        "collapse", "separate", "discard"
+    ] },
+    "text-overline-width": { values: [
+        "normal", "medium", "auto", "thick", "thin"
+    ] },
+    "text-transform": { values: [
+        "none", "capitalize", "uppercase", "lowercase"
+    ] },
+    "border-right-style": { values: [
+        "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+    ] },
+    "border-left-style": { values: [
+        "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+    ] },
+    "-webkit-text-emphasis": { values: [
+        "circle", "filled", "open", "dot", "double-circle", "triangle", "sesame"
+    ] },
+    "font-style": { values: [
+        "italic", "oblique", "normal"
+    ] },
+    "speak": { values: [
+        "none", "normal", "spell-out", "digits", "literal-punctuation", "no-punctuation"
+    ] },
+    "text-line-through": { values: [
+        "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave", "continuous",
+        "skip-white-space"
+    ] },
+    "color-rendering": { values: [
+        "auto", "optimizeSpeed", "optimizeQuality"
+    ] },
+    "list-style-type": { values: [
+        "none", "inline", "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": { values: [
+        "none", "horizontal"
+    ] },
+    "outline": { values: [
+        "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+    ] },
+    "font": { values: [
+        "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"
+    ] },
+    "dominant-baseline": { values: [
+        "middle", "auto", "central", "text-before-edge", "text-after-edge", "ideographic", "alphabetic", "hanging",
+        "mathematical", "use-script", "no-change", "reset-size"
+    ] },
+    "display": { values: [
+        "none", "inline", "block", "list-item", "run-in", "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"
+    ] },
+    "-webkit-text-emphasis-position": { values: [
+        "over", "under"
+    ] },
+    "image-rendering": { values: [
+        "auto", "optimizeSpeed", "optimizeQuality"
+    ] },
+    "alignment-baseline": { values: [
+        "baseline", "middle", "auto", "before-edge", "after-edge", "central", "text-before-edge", "text-after-edge",
+        "ideographic", "alphabetic", "hanging", "mathematical"
+    ] },
+    "outline-width": { values: [
+        "medium", "thick", "thin"
+    ] },
+    "text-line-through-width": { values: [
+        "normal", "medium", "auto", "thick", "thin"
+    ] },
+    "box-align": { values: [
+        "baseline", "center", "stretch", "start", "end"
+    ] },
+    "border-right-width": { values: [
+        "medium", "thick", "thin"
+    ] },
+    "border-top-style": { values: [
+        "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+    ] },
+    "line-height": { values: [
+        "normal"
+    ] },
+    "text-overflow": { values: [
+        "clip", "ellipsis"
+    ] },
+    "overflow-wrap": { values: [
+        "normal", "break-word"
+    ] },
+    "box-direction": { values: [
+        "normal", "reverse"
+    ] },
+    "margin-after-collapse": { values: [
+        "collapse", "separate", "discard"
+    ] },
+    "page-break-before": { values: [
+        "left", "right", "auto", "always", "avoid"
+    ] },
+    "-webkit-hyphens": { values: [
+        "none", "auto", "manual"
+    ] },
+    "border-image": { values: [
+        "repeat", "stretch"
+    ] },
+    "text-decoration": { values: [
+        "blink", "line-through", "overline", "underline"
+    ] },
+    "position": { values: [
+        "absolute", "fixed", "relative", "static"
+    ] },
+    "font-family": { values: [
+        "serif", "sans-serif", "cursive", "fantasy", "monospace", "-webkit-body", "-webkit-pictograph"
+    ] },
+    "text-overflow-mode": { values: [
+        "clip", "ellipsis"
+    ] },
+    "border-bottom-style": { values: [
+        "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+    ] },
+    "unicode-bidi": { values: [
+        "normal", "bidi-override", "embed"
+    ] },
+    "clip-rule": { values: [
+        "nonzero", "evenodd"
+    ] },
+    "margin-left": { values: [
+        "auto"
+    ] },
+    "margin-top": { values: [
+        "auto"
+    ] },
+    "zoom": { values: [
+        "normal", "document", "reset"
+    ] },
+    "text-overline-style": { values: [
+        "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave"
+    ] },
+    "max-width": { values: [
+        "none"
+    ] },
+    "caption-side": { values: [
+        "top", "bottom"
+    ] },
+    "empty-cells": { values: [
+        "hide", "show"
+    ] },
+    "pointer-events": { values: [
+        "none", "all", "auto", "visible", "visiblepainted", "visiblefill", "visiblestroke", "painted", "fill", "stroke"
+    ] },
+    "letter-spacing": { values: [
+        "normal"
+    ] },
+    "background-clip": { values: [
+        "border-box", "content-box", "padding-box"
+    ] },
+    "-webkit-font-smoothing": { values: [
+        "none", "auto", "antialiased", "subpixel-antialiased"
+    ] },
+    "border": { values: [
+        "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double"
+    ] },
+    "font-size": { values: [
+        "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "-webkit-xxx-large", "smaller",
+        "larger"
+    ] },
+    "font-variant": { values: [
+        "small-caps", "normal"
+    ] },
+    "vertical-align": { values: [
+        "baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom", "-webkit-baseline-middle"
+    ] },
+    "marquee-style": { values: [
+        "none", "scroll", "slide", "alternate"
+    ] },
+    "white-space": { values: [
+        "normal", "nowrap", "pre", "pre-line", "pre-wrap"
+    ] },
+    "text-underline-width": { values: [
+        "normal", "medium", "auto", "thick", "thin"
+    ] },
+    "box-lines": { values: [
+        "single", "multiple"
+    ] },
+    "page-break-after": { values: [
+        "left", "right", "auto", "always", "avoid"
+    ] },
+    "clip-path": { values: [
+        "none"
+    ] },
+    "margin": { values: [
+        "auto"
+    ] },
+    "marquee-repetition": { values: [
+        "infinite"
+    ] },
+    "margin-right": { values: [
+        "auto"
+    ] },
+    "word-break": { values: [
+        "normal", "break-all", "break-word"
+    ] },
+    "word-spacing": { values: [
+        "normal"
+    ] },
+    "-webkit-text-emphasis-style": { values: [
+        "circle", "filled", "open", "dot", "double-circle", "triangle", "sesame"
+    ] },
+    "-webkit-transform": { values: [
+        "scale", "scaleX", "scaleY", "scale3d", "rotate", "rotateX", "rotateY", "rotateZ", "rotate3d", "skew", "skewX", "skewY",
+        "translate", "translateX", "translateY", "translateZ", "translate3d", "matrix", "matrix3d", "perspective"
+    ] },
+    "image-resolution": { values: [
+        "from-image", "snap"
+    ] },
+    "box-sizing": { values: [
+        "content-box", "padding-box", "border-box"
+    ] },
+    "clip": { values: [
+        "auto"
+    ] },
+    "resize": { values: [
+        "none", "both", "horizontal", "vertical"
+    ] },
+    "-webkit-align-content": { values: [
+        "flex-start", "flex-end", "center", "space-between", "space-around", "stretch"
+    ] },
+    "-webkit-align-items": {  values: [
+        "flex-start", "flex-end", "center", "baseline", "stretch"
+    ] },
+    "-webkit-align-self": {  values: [
+        "auto", "flex-start", "flex-end", "center", "baseline", "stretch"
+    ] },
+    "-webkit-flex-direction": { values: [
+        "row", "row-reverse", "column", "column-reverse"
+    ] },
+    "-webkit-justify-content": { values: [
+        "flex-start", "flex-end", "center", "space-between", "space-around"
+    ] },
+    "-webkit-flex-wrap": { values: [
+        "nowrap", "wrap", "wrap-reverse"
+    ] },
+    "-webkit-animation-timing-function": { values: [
+        "ease", "linear", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end", "steps", "cubic-bezier"
+    ] },
+    "-webkit-animation-direction": { values: [
+        "normal", "reverse", "alternate", "alternate-reverse"
+    ] },
+    "-webkit-animation-play-state": { values: [
+        "running", "paused"
+    ] },
+    "-webkit-animation-fill-mode": { values: [
+        "none", "forwards", "backwards", "both"
+    ] },
+    "-webkit-backface-visibility": { values: [
+        "visible", "hidden"
+    ] },
+    "-webkit-box-decoration-break": { values: [
+        "slice", "clone"
+    ] },
+    "-webkit-column-break-after": { values: [
+        "auto", "always", "avoid", "left", "right", "page", "column", "avoid-page", "avoid-column"
+    ] },
+    "-webkit-column-break-before": { values: [
+        "auto", "always", "avoid", "left", "right", "page", "column", "avoid-page", "avoid-column"
+    ] },
+    "-webkit-column-break-inside": { values: [
+        "auto", "avoid", "avoid-page", "avoid-column"
+    ] },
+    "-webkit-column-span": { values: [
+        "none", "all"
+    ] },
+    "-webkit-column-count": { values: [
+        "auto"
+    ] },
+    "-webkit-column-gap": { values: [
+        "normal"
+    ] },
+    "-webkit-line-break": { values: [
+        "auto", "loose", "normal", "strict"
+    ] },
+    "-webkit-perspective": { values: [
+        "none"
+    ] },
+    "-webkit-perspective-origin": { values: [
+        "left", "center", "right", "top", "bottom"
+    ] },
+    "-webkit-text-align-last": { values: [
+        "auto", "start", "end", "left", "right", "center", "justify"
+    ] },
+    "-webkit-text-decoration-line": { values: [
+        "none", "underline", "overline", "line-through", "blink"
+    ] },
+    "-webkit-text-decoration-style": { values: [
+        "solid", "double", "dotted", "dashed", "wavy"
+    ] },
+    "-webkit-text-decoration-skip": { values: [
+        "none", "objects", "spaces", "ink", "edges", "box-decoration"
+    ] },
+    "-webkit-transform-origin": { values: [
+        "left", "center", "right", "top", "bottom"
+    ] },
+    "-webkit-transform-style": { values: [
+        "flat", "preserve-3d"
+    ] },
+    "-webkit-transition-timing-function": { values: [
+        "ease", "linear", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end", "steps", "cubic-bezier"
+    ] },
+
+    "-webkit-flex": { m: "flexbox" },
+    "-webkit-flex-basis": { m: "flexbox" },
+    "-webkit-flex-flow": { m: "flexbox" },
+    "-webkit-flex-grow": { m: "flexbox" },
+    "-webkit-flex-shrink": { m: "flexbox" },
+    "-webkit-animation": { m: "animations" },
+    "-webkit-animation-delay": { m: "animations" },
+    "-webkit-animation-duration": { m: "animations" },
+    "-webkit-animation-iteration-count": { m: "animations" },
+    "-webkit-animation-name": { m: "animations" },
+    "-webkit-column-rule": { m: "multicol" },
+    "-webkit-column-rule-color": { m: "multicol", a: "crc" },
+    "-webkit-column-rule-style": { m: "multicol", a: "crs" },
+    "-webkit-column-rule-width": { m: "multicol", a: "crw" },
+    "-webkit-column-width": { m: "multicol", a: "cw" },
+    "-webkit-columns": { m: "multicol" },
+    "-webkit-grid-columns": { m: "grid" },
+    "-webkit-grid-rows": { m: "grid" },
+    "-webkit-order": { m: "flexbox" },
+    "-webkit-text-decoration-color": { m: "text-decor" },
+    "-webkit-text-emphasis-color": { m: "text-decor" },
+    "-webkit-transition": { m: "transitions" },
+    "-webkit-transition-delay": { m: "transitions" },
+    "-webkit-transition-duration": { m: "transitions" },
+    "-webkit-transition-property": { m: "transitions" },
+    "background": { m: "background" },
+    "background-attachment": { m: "background" },
+    "background-color": { m: "background" },
+    "background-image": { m: "background" },
+    "background-position": { m: "background" },
+    "background-position-x": { m: "background" },
+    "background-position-y": { m: "background" },
+    "background-repeat-x": { m: "background" },
+    "background-repeat-y": { m: "background" },
+    "border-top": { m: "background" },
+    "border-right": { m: "background" },
+    "border-bottom": { m: "background" },
+    "border-left": { m: "background" },
+    "border-radius": { m: "background" },
+    "bottom": { m: "visuren" },
+    "box-shadow": { m: "background" },
+    "color": { m: "color", a: "foreground" },
+    "counter-increment": { m: "generate" },
+    "counter-reset": { m: "generate" },
+    "height": { m: "box" },
+    "image-orientation": { m: "images" },
+    "left": { m: "visuren" },
+    "list-style": { m: "lists" },
+    "min-height": { m: "box" },
+    "min-width": { m: "box" },
+    "opacity": { m: "color", a: "transparency" },
+    "orphans": { m: "page" },
+    "outline-offset": { m: "ui" },
+    "padding": { m: "box", a: "padding1" },
+    "padding-bottom": { m: "box" },
+    "padding-left": { m: "box" },
+    "padding-right": { m: "box" },
+    "padding-top": { m: "box" },
+    "page": { m: "page" },
+    "quotes": { m: "generate" },
+    "right": { m: "visuren" },
+    "tab-size": { m: "text" },
+    "text-indent": { m: "text" },
+    "text-shadow": { m: "text-decor" },
+    "top": { m: "visuren" },
+    "unicode-range": { m: "fonts", a: "descdef-unicode-range" },
+    "widows": { m: "page" },
+    "width": { m: "box" },
+    "z-index": { m: "visuren" }
+}
+
+/**
+ * @param {string} propertyName
+ * @return {!WebInspector.CSSMetadata}
+ */
+WebInspector.CSSMetadata.keywordsForProperty = function(propertyName)
+{
+    var acceptedKeywords = ["inherit", "initial"];
+    var descriptor = WebInspector.CSSMetadata.descriptor(propertyName);
+    if (descriptor && descriptor.values)
+        acceptedKeywords.push.apply(acceptedKeywords, descriptor.values);
+    if (propertyName in WebInspector.CSSMetadata._colorAwareProperties)
+        acceptedKeywords.push.apply(acceptedKeywords, WebInspector.CSSMetadata._colors);
+    return new WebInspector.CSSMetadata(acceptedKeywords);
+}
+
+/**
+ * @param {string} propertyName
+ * @return {Object}
+ */
+WebInspector.CSSMetadata.descriptor = function(propertyName)
+{
+    if (!propertyName)
+        return null;
+    var unprefixedName = propertyName.replace(/^-webkit-/, "");
+    var entry = WebInspector.CSSMetadata._propertyDataMap[propertyName];
+    if (!entry && unprefixedName !== propertyName)
+        entry = WebInspector.CSSMetadata._propertyDataMap[unprefixedName];
+    return entry || null;
+}
+
+WebInspector.CSSMetadata.requestCSSShorthandData = function()
+{
+    function propertyNamesCallback(error, properties)
+    {
+        if (!error)
+            WebInspector.CSSMetadata.cssPropertiesMetainfo = new WebInspector.CSSMetadata(properties);
+    }
+    CSSAgent.getSupportedCSSProperties(propertyNamesCallback);
+}
+
+WebInspector.CSSMetadata.cssPropertiesMetainfoKeySet = function()
+{
+    if (!WebInspector.CSSMetadata._cssPropertiesMetainfoKeySet)
+        WebInspector.CSSMetadata._cssPropertiesMetainfoKeySet = WebInspector.CSSMetadata.cssPropertiesMetainfo.keySet();
+    return WebInspector.CSSMetadata._cssPropertiesMetainfoKeySet;
+}
+
+// Weight of CSS properties based their usage on few popular websites https://gist.github.com/3751436
+WebInspector.CSSMetadata.Weight = {
+    "-webkit-animation": 1,
+    "-webkit-animation-duration": 1,
+    "-webkit-animation-iteration-count": 1,
+    "-webkit-animation-name": 1,
+    "-webkit-animation-timing-function": 1,
+    "-webkit-appearance": 1,
+    "-webkit-background-clip": 2,
+    "-webkit-border-horizontal-spacing": 1,
+    "-webkit-border-vertical-spacing": 1,
+    "-webkit-box-shadow": 24,
+    "-webkit-font-smoothing": 2,
+    "-webkit-transform": 1,
+    "-webkit-transition": 8,
+    "-webkit-transition-delay": 7,
+    "-webkit-transition-duration": 7,
+    "-webkit-transition-property": 7,
+    "-webkit-transition-timing-function": 6,
+    "-webkit-user-select": 1,
+    "background": 222,
+    "background-attachment": 144,
+    "background-clip": 143,
+    "background-color": 222,
+    "background-image": 201,
+    "background-origin": 142,
+    "background-size": 25,
+    "border": 121,
+    "border-bottom": 121,
+    "border-bottom-color": 121,
+    "border-bottom-left-radius": 50,
+    "border-bottom-right-radius": 50,
+    "border-bottom-style": 114,
+    "border-bottom-width": 120,
+    "border-collapse": 3,
+    "border-left": 95,
+    "border-left-color": 95,
+    "border-left-style": 89,
+    "border-left-width": 94,
+    "border-radius": 50,
+    "border-right": 93,
+    "border-right-color": 93,
+    "border-right-style": 88,
+    "border-right-width": 93,
+    "border-top": 111,
+    "border-top-color": 111,
+    "border-top-left-radius": 49,
+    "border-top-right-radius": 49,
+    "border-top-style": 104,
+    "border-top-width": 109,
+    "bottom": 16,
+    "box-shadow": 25,
+    "box-sizing": 2,
+    "clear": 23,
+    "color": 237,
+    "cursor": 34,
+    "direction": 4,
+    "display": 210,
+    "fill": 2,
+    "filter": 1,
+    "float": 105,
+    "font": 174,
+    "font-family": 25,
+    "font-size": 174,
+    "font-style": 9,
+    "font-weight": 89,
+    "height": 161,
+    "left": 54,
+    "letter-spacing": 3,
+    "line-height": 75,
+    "list-style": 17,
+    "list-style-image": 8,
+    "list-style-position": 8,
+    "list-style-type": 17,
+    "margin": 241,
+    "margin-bottom": 226,
+    "margin-left": 225,
+    "margin-right": 213,
+    "margin-top": 241,
+    "max-height": 5,
+    "max-width": 11,
+    "min-height": 9,
+    "min-width": 6,
+    "opacity": 24,
+    "outline": 10,
+    "outline-color": 10,
+    "outline-style": 10,
+    "outline-width": 10,
+    "overflow": 57,
+    "overflow-x": 56,
+    "overflow-y": 57,
+    "padding": 216,
+    "padding-bottom": 208,
+    "padding-left": 216,
+    "padding-right": 206,
+    "padding-top": 216,
+    "position": 136,
+    "resize": 1,
+    "right": 29,
+    "stroke": 1,
+    "stroke-width": 1,
+    "table-layout": 1,
+    "text-align": 66,
+    "text-decoration": 53,
+    "text-indent": 9,
+    "text-overflow": 8,
+    "text-shadow": 19,
+    "text-transform": 5,
+    "top": 71,
+    "unicode-bidi": 1,
+    "vertical-align": 37,
+    "visibility": 11,
+    "white-space": 24,
+    "width": 255,
+    "word-wrap": 6,
+    "z-index": 32,
+    "zoom": 10
+};
+
+
+WebInspector.CSSMetadata.prototype = {
+    /**
+     * @param {string} prefix
+     * @return {!Array.<string>}
+     */
+    startsWith: function(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;
+    },
+
+    /**
+     * @param {Array.<string>} properties
+     * @return {number}
+     */
+    mostUsedOf: function(properties)
+    {
+        var maxWeight = 0;
+        var index = 0;
+        for (var i = 0; i < properties.length; i++) {
+            var weight = WebInspector.CSSMetadata.Weight[properties[i]];
+            if (weight > maxWeight) {
+                maxWeight = weight;
+                index = i;
+            }
+        }
+        return index;
+    },
+
+    _firstIndexOfPrefix: function(prefix)
+    {
+        if (!this._values.length)
+            return -1;
+        if (!prefix)
+            return 0;
+
+        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: function()
+    {
+        if (!this._keySet)
+            this._keySet = this._values.keySet();
+        return this._keySet;
+    },
+
+    next: function(str, prefix)
+    {
+        return this._closest(str, prefix, 1);
+    },
+
+    previous: function(str, prefix)
+    {
+        return this._closest(str, prefix, -1);
+    },
+
+    _closest: function(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];
+    },
+
+    /**
+     * @param {string} shorthand
+     * @return {?Array.<string>}
+     */
+    longhands: function(shorthand)
+    {
+        return this._longhands[shorthand];
+    },
+
+    /**
+     * @param {string} longhand
+     * @return {?Array.<string>}
+     */
+    shorthands: function(longhand)
+    {
+        return this._shorthands[longhand];
+    }
+}
diff --git a/Source/devtools/front_end/CSSNamedFlowCollectionsView.js b/Source/devtools/front_end/CSSNamedFlowCollectionsView.js
new file mode 100644
index 0000000..d420d27
--- /dev/null
+++ b/Source/devtools/front_end/CSSNamedFlowCollectionsView.js
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2012 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 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarView}
+ */
+WebInspector.CSSNamedFlowCollectionsView = function()
+{
+    WebInspector.SidebarView.call(this, WebInspector.SidebarView.SidebarPosition.Start);
+    this.registerRequiredCSS("cssNamedFlows.css");
+
+    this._namedFlows = {};
+    this._contentNodes = {};
+    this._regionNodes = {};
+
+    this.element.addStyleClass("css-named-flow-collections-view");
+    this.element.addStyleClass("fill");
+
+    this._statusElement = document.createElement("span");
+    this._statusElement.textContent = WebInspector.UIString("CSS Named Flows");
+
+    var sidebarHeader = this.firstElement().createChild("div", "tabbed-pane-header selected sidebar-header");
+    var tab = sidebarHeader.createChild("div", "tabbed-pane-header-tab");
+    tab.createChild("span", "tabbed-pane-header-tab-title").textContent = WebInspector.UIString("CSS Named Flows");
+
+    this._sidebarContentElement = this.firstElement().createChild("div", "sidebar-content outline-disclosure");
+    this._flowListElement = this._sidebarContentElement.createChild("ol");
+    this._flowTree = new TreeOutline(this._flowListElement);
+
+    this._emptyElement = document.createElement("div");
+    this._emptyElement.addStyleClass("info");
+    this._emptyElement.textContent = WebInspector.UIString("No CSS Named Flows");
+
+    this._tabbedPane = new WebInspector.TabbedPane();
+    this._tabbedPane.closeableTabs = true;
+    this._tabbedPane.show(this.secondElement());
+}
+
+WebInspector.CSSNamedFlowCollectionsView.prototype = {
+    showInDrawer: function()
+    {
+        WebInspector.showViewInDrawer(this._statusElement, this);
+    },
+
+    reset: function()
+    {
+        if (!this._document)
+            return;
+
+        WebInspector.cssModel.getNamedFlowCollectionAsync(this._document.id, this._resetNamedFlows.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.DOMDocument} document
+     */
+    _setDocument: function(document)
+    {
+        this._document = document;
+        this.reset();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _documentUpdated: function(event)
+    {
+        var document = /** @type {WebInspector.DOMDocument} */ (event.data);
+        this._setDocument(document);
+    },
+
+    /**
+     * @param {boolean} hasContent
+     */
+    _setSidebarHasContent: function(hasContent)
+    {
+        if (hasContent) {
+            if (!this._emptyElement.parentNode)
+                return;
+
+            this._sidebarContentElement.removeChild(this._emptyElement);
+            this._sidebarContentElement.appendChild(this._flowListElement);
+        } else {
+            if (!this._flowListElement.parentNode)
+                return;
+
+            this._sidebarContentElement.removeChild(this._flowListElement);
+            this._sidebarContentElement.appendChild(this._emptyElement);
+        }
+    },
+
+    /**
+     * @param {WebInspector.NamedFlow} flow
+     */
+    _appendNamedFlow: function(flow)
+    {
+        var flowHash = this._hashNamedFlow(flow.documentNodeId, flow.name);
+        var flowContainer = { flow: flow, flowHash: flowHash };
+
+        for (var i = 0; i < flow.content.length; ++i)
+            this._contentNodes[flow.content[i]] = flowHash;
+        for (var i = 0; i < flow.regions.length; ++i)
+            this._regionNodes[flow.regions[i].nodeId] = flowHash;
+
+        var flowTreeItem = new WebInspector.FlowTreeElement(flowContainer);
+        flowTreeItem.onselect = this._selectNamedFlowTab.bind(this, flowHash);
+
+        flowContainer.flowTreeItem = flowTreeItem;
+        this._namedFlows[flowHash] = flowContainer;
+
+        if (!this._flowTree.children.length)
+            this._setSidebarHasContent(true);
+        this._flowTree.appendChild(flowTreeItem);
+    },
+
+    /**
+     * @param {string} flowHash
+     */
+    _removeNamedFlow: function(flowHash)
+    {
+        var flowContainer = this._namedFlows[flowHash];
+
+        if (this._tabbedPane._tabsById[flowHash])
+            this._tabbedPane.closeTab(flowHash);
+        this._flowTree.removeChild(flowContainer.flowTreeItem);
+
+        var flow = flowContainer.flow;
+        for (var i = 0; i < flow.content.length; ++i)
+            delete this._contentNodes[flow.content[i]];
+        for (var i = 0; i < flow.regions.length; ++i)
+            delete this._regionNodes[flow.regions[i].nodeId];
+
+        delete this._namedFlows[flowHash];
+
+        if (!this._flowTree.children.length)
+            this._setSidebarHasContent(false);
+    },
+
+    /**
+     * @param {WebInspector.NamedFlow} flow
+     */
+    _updateNamedFlow: function(flow)
+    {
+        var flowHash = this._hashNamedFlow(flow.documentNodeId, flow.name);
+        var flowContainer = this._namedFlows[flowHash];
+
+        if (!flowContainer)
+            return;
+
+        var oldFlow = flowContainer.flow;
+        flowContainer.flow = flow;
+
+        for (var i = 0; i < oldFlow.content.length; ++i)
+            delete this._contentNodes[oldFlow.content[i]];
+        for (var i = 0; i < oldFlow.regions.length; ++i)
+            delete this._regionNodes[oldFlow.regions[i].nodeId];
+
+        for (var i = 0; i < flow.content.length; ++i)
+            this._contentNodes[flow.content[i]] = flowHash;
+        for (var i = 0; i < flow.regions.length; ++i)
+            this._regionNodes[flow.regions[i].nodeId] = flowHash;
+
+        flowContainer.flowTreeItem.setOverset(flow.overset);
+
+        if (flowContainer.flowView)
+            flowContainer.flowView.flow = flow;
+    },
+
+    /**
+     * @param {WebInspector.NamedFlowCollection} namedFlowCollection
+     */
+    _resetNamedFlows: function(namedFlowCollection)
+    {
+        for (var flowHash in this._namedFlows)
+            this._removeNamedFlow(flowHash);
+
+        var namedFlows = namedFlowCollection.namedFlowMap;
+        for (var flowName in namedFlows)
+            this._appendNamedFlow(namedFlows[flowName]);
+
+        if (!this._flowTree.children.length)
+            this._setSidebarHasContent(false);
+        else
+            this._showNamedFlowForNode(WebInspector.panel("elements").treeOutline.selectedDOMNode());
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _namedFlowCreated: function(event)
+    {
+        // FIXME: We only have support for Named Flows in the main document.
+        if (event.data.documentNodeId !== this._document.id)
+            return;
+
+        var flow = /** @type {WebInspector.NamedFlow} */ (event.data);
+        this._appendNamedFlow(flow);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _namedFlowRemoved: function(event)
+    {
+        // FIXME: We only have support for Named Flows in the main document.
+        if (event.data.documentNodeId !== this._document.id)
+            return;
+
+        this._removeNamedFlow(this._hashNamedFlow(event.data.documentNodeId, event.data.flowName));
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _regionLayoutUpdated: function(event)
+    {
+        // FIXME: We only have support for Named Flows in the main document.
+        if (event.data.documentNodeId !== this._document.id)
+            return;
+
+        var flow = /** @type {WebInspector.NamedFlow} */ (event.data);
+        this._updateNamedFlow(flow);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} documentNodeId
+     * @param {string} flowName
+     */
+    _hashNamedFlow: function(documentNodeId, flowName)
+    {
+        return documentNodeId + "|" + flowName;
+    },
+
+    /**
+     * @param {string} flowHash
+     */
+    _showNamedFlow: function(flowHash)
+    {
+        this._selectNamedFlowInSidebar(flowHash);
+        this._selectNamedFlowTab(flowHash);
+    },
+
+    /**
+     * @param {string} flowHash
+     */
+    _selectNamedFlowInSidebar: function(flowHash)
+    {
+        this._namedFlows[flowHash].flowTreeItem.select(true);
+    },
+
+    /**
+     * @param {string} flowHash
+     */
+    _selectNamedFlowTab: function(flowHash)
+    {
+        var flowContainer = this._namedFlows[flowHash];
+
+        if (this._tabbedPane.selectedTabId === flowHash)
+            return;
+
+        if (!this._tabbedPane.selectTab(flowHash)) {
+            if (!flowContainer.flowView)
+                flowContainer.flowView = new WebInspector.CSSNamedFlowView(flowContainer.flow);
+
+            this._tabbedPane.appendTab(flowHash, flowContainer.flow.name, flowContainer.flowView);
+            this._tabbedPane.selectTab(flowHash);
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _selectedNodeChanged: function(event)
+    {
+        var node = /** @type {WebInspector.DOMNode} */ (event.data);
+        this._showNamedFlowForNode(node);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _tabSelected: function(event)
+    {
+        this._selectNamedFlowInSidebar(event.data.tabId);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _tabClosed: function(event)
+    {
+        this._namedFlows[event.data.tabId].flowTreeItem.deselect();
+    },
+
+    /**
+     * @param {?WebInspector.DOMNode} node
+     */
+    _showNamedFlowForNode: function(node)
+    {
+        if (!node)
+            return;
+
+        if (this._regionNodes[node.id]) {
+            this._showNamedFlow(this._regionNodes[node.id]);
+            return;
+        }
+
+        while (node) {
+            if (this._contentNodes[node.id]) {
+                this._showNamedFlow(this._contentNodes[node.id]);
+                return;
+            }
+
+            node = node.parentNode;
+        }
+    },
+
+    wasShown: function()
+    {
+        WebInspector.SidebarView.prototype.wasShown.call(this);
+
+        WebInspector.domAgent.requestDocument(this._setDocument.bind(this));
+
+        WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdated, this);
+
+        WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.NamedFlowCreated, this._namedFlowCreated, this);
+        WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, this._namedFlowRemoved, this);
+        WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, this._regionLayoutUpdated, this);
+
+        WebInspector.panel("elements").treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this);
+
+        this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
+        this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._tabClosed, this);
+    },
+
+    willHide: function()
+    {
+        WebInspector.domAgent.removeEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdated, this);
+
+        WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.NamedFlowCreated, this._namedFlowCreated, this);
+        WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, this._namedFlowRemoved, this);
+        WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, this._regionLayoutUpdated, this);
+
+        WebInspector.panel("elements").treeOutline.removeEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this);
+
+        this._tabbedPane.removeEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
+        this._tabbedPane.removeEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._tabClosed, this);
+    },
+
+    __proto__: WebInspector.SidebarView.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ */
+WebInspector.FlowTreeElement = function(flowContainer)
+{
+    var container = document.createElement("div");
+    container.createChild("div", "selection");
+    container.createChild("span", "title").createChild("span").textContent = flowContainer.flow.name;
+
+    TreeElement.call(this, container, flowContainer, false);
+
+    this._overset = false;
+    this.setOverset(flowContainer.flow.overset);
+}
+
+WebInspector.FlowTreeElement.prototype = {
+    /**
+     * @param {boolean} newOverset
+     */
+    setOverset: function(newOverset)
+    {
+        if (this._overset === newOverset)
+            return;
+
+        if (newOverset) {
+            this.title.addStyleClass("named-flow-overflow");
+            this.tooltip = WebInspector.UIString("Overflows.");
+        } else {
+            this.title.removeStyleClass("named-flow-overflow");
+            this.tooltip = "";
+        }
+
+        this._overset = newOverset;
+    },
+
+    __proto__: TreeElement.prototype
+}
diff --git a/Source/devtools/front_end/CSSNamedFlowView.js b/Source/devtools/front_end/CSSNamedFlowView.js
new file mode 100644
index 0000000..ade2643
--- /dev/null
+++ b/Source/devtools/front_end/CSSNamedFlowView.js
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2012 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 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.NamedFlow} flow
+ */
+WebInspector.CSSNamedFlowView = function(flow)
+{
+    WebInspector.View.call(this);
+    this.element.addStyleClass("css-named-flow");
+    this.element.addStyleClass("outline-disclosure");
+
+    this._treeOutline = new TreeOutline(this.element.createChild("ol"), true);
+
+    this._contentTreeItem = new TreeElement(WebInspector.UIString("content"), null, true);
+    this._treeOutline.appendChild(this._contentTreeItem);
+
+    this._regionsTreeItem = new TreeElement(WebInspector.UIString("region chain"), null, true);
+    this._regionsTreeItem.expand();
+    this._treeOutline.appendChild(this._regionsTreeItem);
+
+    this._flow = flow;
+
+    var content = flow.content;
+    for (var i = 0; i < content.length; ++i)
+        this._insertContentNode(content[i]);
+
+    var regions = flow.regions;
+    for (var i = 0; i < regions.length; ++i)
+        this._insertRegion(regions[i]);
+}
+
+WebInspector.CSSNamedFlowView.OversetTypeMessageMap = {
+    empty: "empty",
+    fit: "fit",
+    overset: "overset"
+}
+
+WebInspector.CSSNamedFlowView.prototype = {
+    /**
+     * @param {WebInspector.DOMNode=} rootDOMNode
+     * @return {?WebInspector.ElementsTreeOutline}
+     */
+    _createFlowTreeOutline: function(rootDOMNode)
+    {
+        if (!rootDOMNode)
+            return null;
+
+        var treeOutline = new WebInspector.ElementsTreeOutline(false, false, true);
+        treeOutline.element.addStyleClass("named-flow-element");
+        treeOutline.setVisible(true);
+        treeOutline.rootDOMNode = rootDOMNode;
+        treeOutline.wireToDomAgent();
+        WebInspector.domAgent.removeEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, treeOutline._elementsTreeUpdater._documentUpdated, treeOutline._elementsTreeUpdater);
+
+        return treeOutline;
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} contentNodeId
+     * @param {number=} index
+     */
+    _insertContentNode: function(contentNodeId, index)
+    {
+        var treeOutline = this._createFlowTreeOutline(WebInspector.domAgent.nodeForId(contentNodeId));
+        var treeItem = new TreeElement(treeOutline.element, treeOutline);
+
+        if (index === undefined) {
+            this._contentTreeItem.appendChild(treeItem);
+            return;
+        }
+
+        this._contentTreeItem.insertChild(treeItem, index);
+    },
+
+    /**
+     * @param {CSSAgent.Region} region
+     * @param {number=} index
+     */
+    _insertRegion: function(region, index)
+    {
+        var treeOutline = this._createFlowTreeOutline(WebInspector.domAgent.nodeForId(region.nodeId));
+        treeOutline.element.addStyleClass("region-" + region.regionOverset);
+
+        var treeItem = new TreeElement(treeOutline.element, treeOutline);
+        var oversetText = WebInspector.UIString(WebInspector.CSSNamedFlowView.OversetTypeMessageMap[region.regionOverset]);
+        treeItem.tooltip = WebInspector.UIString("Region is %s.", oversetText);
+
+        if (index === undefined) {
+            this._regionsTreeItem.appendChild(treeItem);
+            return;
+        }
+
+        this._regionsTreeItem.insertChild(treeItem, index);
+    },
+
+    get flow()
+    {
+        return this._flow;
+    },
+
+    set flow(newFlow)
+    {
+        this._update(newFlow);
+    },
+
+    /**
+     * @param {TreeElement} regionTreeItem
+     * @param {string} newRegionOverset
+     * @param {string} oldRegionOverset
+     */
+    _updateRegionOverset: function(regionTreeItem, newRegionOverset, oldRegionOverset)
+    {
+        var element = regionTreeItem.representedObject.element;
+        element.removeStyleClass("region-" + oldRegionOverset);
+        element.addStyleClass("region-" + newRegionOverset);
+
+        var oversetText = WebInspector.UIString(WebInspector.CSSNamedFlowView.OversetTypeMessageMap[newRegionOverset]);
+        regionTreeItem.tooltip = WebInspector.UIString("Region is %s." , oversetText);
+    },
+
+    /**
+     * @param {Array.<DOMAgent.NodeId>} oldContent
+     * @param {Array.<DOMAgent.NodeId>} newContent
+     */
+    _mergeContentNodes: function(oldContent, newContent)
+    {
+        var nodeIdSet = {};
+        for (var i = 0; i < newContent.length; ++i)
+            nodeIdSet[newContent[i]] = true;
+
+        var oldContentIndex = 0;
+        var newContentIndex = 0;
+        var contentTreeChildIndex = 0;
+
+        while(oldContentIndex < oldContent.length || newContentIndex < newContent.length) {
+            if (oldContentIndex === oldContent.length) {
+                this._insertContentNode(newContent[newContentIndex]);
+                ++newContentIndex;
+                continue;
+            }
+
+            if (newContentIndex === newContent.length) {
+                this._contentTreeItem.removeChildAtIndex(contentTreeChildIndex);
+                ++oldContentIndex;
+                continue;
+            }
+
+            if (oldContent[oldContentIndex] === newContent[newContentIndex]) {
+                ++oldContentIndex;
+                ++newContentIndex;
+                ++contentTreeChildIndex;
+                continue;
+            }
+
+            if (nodeIdSet[oldContent[oldContentIndex]]) {
+                this._insertContentNode(newContent[newContentIndex], contentTreeChildIndex);
+                ++newContentIndex;
+                ++contentTreeChildIndex;
+                continue;
+            }
+
+            this._contentTreeItem.removeChildAtIndex(contentTreeChildIndex);
+            ++oldContentIndex;
+        }
+    },
+
+    /**
+     * @param {Array.<CSSAgent.Region>} oldRegions
+     * @param {Array.<CSSAgent.Region>} newRegions
+     */
+    _mergeRegions: function(oldRegions, newRegions)
+    {
+        var nodeIdSet = {};
+        for (var i = 0; i < newRegions.length; ++i)
+            nodeIdSet[newRegions[i].nodeId] = true;
+
+        var oldRegionsIndex = 0;
+        var newRegionsIndex = 0;
+        var regionsTreeChildIndex = 0;
+
+        while(oldRegionsIndex < oldRegions.length || newRegionsIndex < newRegions.length) {
+            if (oldRegionsIndex === oldRegions.length) {
+                this._insertRegion(newRegions[newRegionsIndex]);
+                ++newRegionsIndex;
+                continue;
+            }
+
+            if (newRegionsIndex === newRegions.length) {
+                this._regionsTreeItem.removeChildAtIndex(regionsTreeChildIndex);
+                ++oldRegionsIndex;
+                continue;
+            }
+
+            if (oldRegions[oldRegionsIndex].nodeId === newRegions[newRegionsIndex].nodeId) {
+                if (oldRegions[oldRegionsIndex].regionOverset !== newRegions[newRegionsIndex].regionOverset)
+                    this._updateRegionOverset(this._regionsTreeItem.children[regionsTreeChildIndex], newRegions[newRegionsIndex].regionOverset, oldRegions[oldRegionsIndex].regionOverset);
+                ++oldRegionsIndex;
+                ++newRegionsIndex;
+                ++regionsTreeChildIndex;
+                continue;
+            }
+
+            if (nodeIdSet[oldRegions[oldRegionsIndex].nodeId]) {
+                this._insertRegion(newRegions[newRegionsIndex], regionsTreeChildIndex);
+                ++newRegionsIndex;
+                ++regionsTreeChildIndex;
+                continue;
+            }
+
+            this._regionsTreeItem.removeChildAtIndex(regionsTreeChildIndex);
+            ++oldRegionsIndex;
+        }
+    },
+
+    /**
+     * @param {WebInspector.NamedFlow} newFlow
+     */
+    _update: function(newFlow)
+    {
+        this._mergeContentNodes(this._flow.content, newFlow.content);
+        this._mergeRegions(this._flow.regions, newFlow.regions);
+
+        this._flow = newFlow;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/CSSSelectorProfileView.js b/Source/devtools/front_end/CSSSelectorProfileView.js
new file mode 100644
index 0000000..adfb4d3
--- /dev/null
+++ b/Source/devtools/front_end/CSSSelectorProfileView.js
@@ -0,0 +1,399 @@
+/*
+ * 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends WebInspector.DataGridNode
+ * @param {WebInspector.CSSSelectorProfileView} profileView
+ * @param {CSSAgent.SelectorProfile} data
+ */
+WebInspector.CSSSelectorDataGridNode = function(profileView, data)
+{
+    WebInspector.DataGridNode.call(this, data, false);
+    this._profileView = profileView;
+}
+
+WebInspector.CSSSelectorDataGridNode.prototype = {
+    get data()
+    {
+        var data = {};
+        data.selector = this._data.selector;
+        data.matches = this._data.matchCount;
+
+        if (this._profileView.showTimeAsPercent.get())
+            data.time = Number(this._data.timePercent).toFixed(1) + "%";
+        else
+            data.time = Number.secondsToString(this._data.time / 1000, true);
+
+        return data;
+    },
+
+    get rawData()
+    {
+        return this._data;
+    },
+
+    createCell: function(columnIdentifier)
+    {
+        var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
+        if (columnIdentifier === "selector" && cell.firstChild) {
+            cell.firstChild.title = this.rawData.selector;
+            return cell;
+        }
+
+        if (columnIdentifier !== "source")
+            return cell;
+
+        cell.removeChildren();
+
+        if (this.rawData.url) {
+            var wrapperDiv = cell.createChild("div");
+            wrapperDiv.appendChild(WebInspector.linkifyResourceAsNode(this.rawData.url, this.rawData.lineNumber));
+        }
+
+        return cell;
+    },
+
+    __proto__: WebInspector.DataGridNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends WebInspector.View
+ * @param {CSSAgent.SelectorProfile} profile
+ */
+WebInspector.CSSSelectorProfileView = function(profile)
+{
+    WebInspector.View.call(this);
+
+    this.element.addStyleClass("profile-view");
+
+    this.showTimeAsPercent = WebInspector.settings.createSetting("selectorProfilerShowTimeAsPercent", true);
+
+    var columns = [
+        {id: "selector", title: WebInspector.UIString("Selector"), width: "550px", sortable: true},
+        {id: "source", title: WebInspector.UIString("Source"), width: "100px", sortable: true},
+        {id: "time", title: WebInspector.UIString("Total"), width: "72px", sort: WebInspector.DataGrid.Order.Descending, sortable: true},
+        {id: "matches", title: WebInspector.UIString("Matches"), width: "72px", sortable: true}
+    ];
+
+    this.dataGrid = new WebInspector.DataGrid(columns);
+    this.dataGrid.element.addStyleClass("selector-profile-view");
+    this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortProfile, this);
+    this.dataGrid.element.addEventListener("mousedown", this._mouseDownInDataGrid.bind(this), true);
+    this.dataGrid.show(this.element);
+
+    this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item");
+    this.percentButton.addEventListener("click", this._percentClicked, this);
+
+    this.profile = profile;
+
+    this._createProfileNodes();
+    this._sortProfile();
+    this._updatePercentButton();
+}
+
+WebInspector.CSSSelectorProfileView.prototype = {
+    get statusBarItems()
+    {
+        return [this.percentButton.element];
+    },
+
+    get profile()
+    {
+        return this._profile;
+    },
+
+    set profile(profile)
+    {
+        this._profile = profile;
+    },
+
+    _createProfileNodes: function()
+    {
+        var data = this.profile.data;
+        if (!data) {
+            // The profiler may have been terminated with the "Clear all profiles." button.
+            return;
+        }
+
+        this.profile.children = [];
+        for (var i = 0; i < data.length; ++i) {
+            data[i].timePercent = data[i].time * 100 / this.profile.totalTime;
+            var node = new WebInspector.CSSSelectorDataGridNode(this, data[i]);
+            this.profile.children.push(node);
+        }
+    },
+
+    rebuildGridItems: function()
+    {
+        this.dataGrid.rootNode().removeChildren();
+
+        var children = this.profile.children;
+        var count = children.length;
+
+        for (var index = 0; index < count; ++index)
+            this.dataGrid.rootNode().appendChild(children[index]);
+    },
+
+    refreshData: function()
+    {
+        var child = this.dataGrid.rootNode().children[0];
+        while (child) {
+            child.refresh();
+            child = child.traverseNextNode(false, null, true);
+        }
+    },
+
+    refreshShowAsPercents: function()
+    {
+        this._updatePercentButton();
+        this.refreshData();
+    },
+
+    _percentClicked: function(event)
+    {
+        this.showTimeAsPercent.set(!this.showTimeAsPercent.get());
+        this.refreshShowAsPercents();
+    },
+
+    _updatePercentButton: function()
+    {
+        if (this.showTimeAsPercent.get()) {
+            this.percentButton.title = WebInspector.UIString("Show absolute times.");
+            this.percentButton.toggled = true;
+        } else {
+            this.percentButton.title = WebInspector.UIString("Show times as percentages.");
+            this.percentButton.toggled = false;
+        }
+    },
+
+    _sortProfile: function()
+    {
+        var sortAscending = this.dataGrid.isSortOrderAscending();
+        var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier();
+
+        function selectorComparator(a, b)
+        {
+            var result = b.rawData.selector.compareTo(a.rawData.selector);
+            return sortAscending ? -result : result;
+        }
+
+        function sourceComparator(a, b)
+        {
+            var aRawData = a.rawData;
+            var bRawData = b.rawData;
+            var result = bRawData.url.compareTo(aRawData.url);
+            if (!result)
+                result = bRawData.lineNumber - aRawData.lineNumber;
+            return sortAscending ? -result : result;
+        }
+
+        function timeComparator(a, b)
+        {
+            const result = b.rawData.time - a.rawData.time;
+            return sortAscending ? -result : result;
+        }
+
+        function matchesComparator(a, b)
+        {
+            const result = b.rawData.matchCount - a.rawData.matchCount;
+            return sortAscending ? -result : result;
+        }
+
+        var comparator;
+        switch (sortColumnIdentifier) {
+        case "time":
+            comparator = timeComparator;
+            break;
+        case "matches":
+            comparator = matchesComparator;
+            break;
+        case "selector":
+            comparator = selectorComparator;
+            break;
+        case "source":
+            comparator = sourceComparator;
+            break;
+        }
+
+        this.profile.children.sort(comparator);
+
+        this.rebuildGridItems();
+    },
+
+    _mouseDownInDataGrid: function(event)
+    {
+        if (event.detail < 2)
+            return;
+
+        var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
+        if (!cell)
+            return;
+
+        if (cell.hasStyleClass("time-column"))
+            this.showTimeAsPercent.set(!this.showTimeAsPercent.get());
+        else
+            return;
+
+        this.refreshShowAsPercents();
+
+        event.consume(true);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileType}
+ */
+WebInspector.CSSSelectorProfileType = function()
+{
+    WebInspector.ProfileType.call(this, WebInspector.CSSSelectorProfileType.TypeId, WebInspector.UIString("Collect CSS Selector Profile"));
+    this._recording = false;
+    this._profileUid = 1;
+    WebInspector.CSSSelectorProfileType.instance = this;
+}
+
+WebInspector.CSSSelectorProfileType.TypeId = "SELECTOR";
+
+WebInspector.CSSSelectorProfileType.prototype = {
+    get buttonTooltip()
+    {
+        return this._recording ? WebInspector.UIString("Stop CSS selector profiling.") : WebInspector.UIString("Start CSS selector profiling.");
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    buttonClicked: function()
+    {
+        if (this._recording) {
+            this._stopRecordingProfile();
+            return false;
+        } else {
+            this._startRecordingProfile();
+            return true;
+        }
+    },
+
+    get treeItemTitle()
+    {
+        return WebInspector.UIString("CSS SELECTOR PROFILES");
+    },
+
+    get description()
+    {
+        return WebInspector.UIString("CSS selector profiles show how long the selector matching has taken in total and how many times a certain selector has matched DOM elements. The results are approximate due to matching algorithm optimizations.");
+    },
+
+    reset: function()
+    {
+        this._profileUid = 1;
+    },
+
+    setRecordingProfile: function(isProfiling)
+    {
+        this._recording = isProfiling;
+    },
+
+    _startRecordingProfile: function()
+    {
+        this._recording = true;
+        CSSAgent.startSelectorProfiler();
+    },
+
+    _stopRecordingProfile: function()
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {CSSAgent.SelectorProfile} profile
+         */
+        function callback(error, profile)
+        {
+            if (error)
+                return;
+
+            var uid = this._profileUid++;
+            var title = WebInspector.UIString("Profile %d", uid) + String.sprintf(" (%s)", Number.secondsToString(profile.totalTime / 1000));
+            this.addProfile(new WebInspector.CSSProfileHeader(this, title, uid, profile));
+        }
+
+        this._recording = false;
+        CSSAgent.stopSelectorProfiler(callback.bind(this));
+    },
+
+    /**
+     * @override
+     * @param {string=} title
+     * @return {WebInspector.ProfileHeader}
+     */
+    createTemporaryProfile: function(title)
+    {
+        title = title || WebInspector.UIString("Recording\u2026");
+        return new WebInspector.CSSProfileHeader(this, title);
+    },
+
+    __proto__: WebInspector.ProfileType.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileHeader}
+ * @param {!WebInspector.CSSSelectorProfileType} type
+ * @param {string} title
+ * @param {number=} uid
+ * @param {CSSAgent.SelectorProfile=} protocolData
+ */
+WebInspector.CSSProfileHeader = function(type, title, uid, protocolData)
+{
+    WebInspector.ProfileHeader.call(this, type, title, uid);
+    this._protocolData = protocolData;
+}
+
+WebInspector.CSSProfileHeader.prototype = {
+    /**
+     * @override
+     */
+    createSidebarTreeElement: function()
+    {
+        return new WebInspector.ProfileSidebarTreeElement(this, this.title, "profile-sidebar-tree-item");
+    },
+
+    /**
+     * @override
+     * @param {WebInspector.ProfilesPanel} profilesPanel
+     */
+    createView: function(profilesPanel)
+    {
+        var profile = /** @type {CSSAgent.SelectorProfile} */ (this._protocolData);
+        return new WebInspector.CSSSelectorProfileView(profile);
+    },
+
+    __proto__: WebInspector.ProfileHeader.prototype
+}
diff --git a/Source/devtools/front_end/CSSStyleModel.js b/Source/devtools/front_end/CSSStyleModel.js
new file mode 100644
index 0000000..d85e54f
--- /dev/null
+++ b/Source/devtools/front_end/CSSStyleModel.js
@@ -0,0 +1,1577 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.CSSStyleModel = function(workspace)
+{
+    this._workspace = workspace;
+    this._pendingCommandsMajorState = [];
+    /** @type {Array.<WebInspector.CSSStyleModel.LiveLocation>} */
+    this._locations = [];
+    this._sourceMappings = {};
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoRequested, this._undoRedoRequested, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoCompleted, this._undoRedoCompleted, this);
+    this._resourceBinding = new WebInspector.CSSStyleModelResourceBinding();
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameCreatedOrNavigated, this._mainFrameCreatedOrNavigated, this);
+    this._namedFlowCollections = {};
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._resetNamedFlowCollections, this);
+    InspectorBackend.registerCSSDispatcher(new WebInspector.CSSDispatcher(this));
+    CSSAgent.enable();
+}
+
+/**
+ * @param {Array.<CSSAgent.CSSRule>} ruleArray
+ */
+WebInspector.CSSStyleModel.parseRuleArrayPayload = function(ruleArray)
+{
+    var result = [];
+    for (var i = 0; i < ruleArray.length; ++i)
+        result.push(WebInspector.CSSRule.parsePayload(ruleArray[i]));
+    return result;
+}
+    
+/**
+ * @param {Array.<CSSAgent.RuleMatch>} matchArray
+ */
+WebInspector.CSSStyleModel.parseRuleMatchArrayPayload = function(matchArray)
+{
+    var result = [];
+    for (var i = 0; i < matchArray.length; ++i)
+        result.push(WebInspector.CSSRule.parsePayload(matchArray[i].rule, matchArray[i].matchingSelectors));
+    return result;
+}
+
+WebInspector.CSSStyleModel.Events = {
+    StyleSheetAdded: "StyleSheetAdded",
+    StyleSheetChanged: "StyleSheetChanged",
+    StyleSheetRemoved: "StyleSheetRemoved",
+    MediaQueryResultChanged: "MediaQueryResultChanged",
+    NamedFlowCreated: "NamedFlowCreated",
+    NamedFlowRemoved: "NamedFlowRemoved",
+    RegionLayoutUpdated: "RegionLayoutUpdated"
+}
+
+WebInspector.CSSStyleModel.MediaTypes = ["all", "braille", "embossed", "handheld", "print", "projection", "screen", "speech", "tty", "tv"];
+
+WebInspector.CSSStyleModel.prototype = {
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {boolean} needPseudo
+     * @param {boolean} needInherited
+     * @param {function(?*)} userCallback
+     */
+    getMatchedStylesAsync: function(nodeId, needPseudo, needInherited, userCallback)
+    {
+        /**
+         * @param {function(?*)} userCallback
+         * @param {?Protocol.Error} error
+         * @param {Array.<CSSAgent.RuleMatch>=} matchedPayload
+         * @param {Array.<CSSAgent.PseudoIdMatches>=} pseudoPayload
+         * @param {Array.<CSSAgent.InheritedStyleEntry>=} inheritedPayload
+         */
+        function callback(userCallback, error, matchedPayload, pseudoPayload, inheritedPayload)
+        {
+            if (error) {
+                if (userCallback)
+                    userCallback(null);
+                return;
+            }
+
+            var result = {};
+            if (matchedPayload)
+                result.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(matchedPayload);
+
+            if (pseudoPayload) {
+                result.pseudoElements = [];
+                for (var i = 0; i < pseudoPayload.length; ++i) {
+                    var entryPayload = pseudoPayload[i];
+                    result.pseudoElements.push({ pseudoId: entryPayload.pseudoId, rules: WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(entryPayload.matches) });
+                }
+            }
+
+            if (inheritedPayload) {
+                result.inherited = [];
+                for (var i = 0; i < inheritedPayload.length; ++i) {
+                    var entryPayload = inheritedPayload[i];
+                    var entry = {};
+                    if (entryPayload.inlineStyle)
+                        entry.inlineStyle = WebInspector.CSSStyleDeclaration.parsePayload(entryPayload.inlineStyle);
+                    if (entryPayload.matchedCSSRules)
+                        entry.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(entryPayload.matchedCSSRules);
+                    result.inherited.push(entry);
+                }
+            }
+
+            if (userCallback)
+                userCallback(result);
+        }
+
+        CSSAgent.getMatchedStylesForNode(nodeId, needPseudo, needInherited, callback.bind(null, userCallback));
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {function(?WebInspector.CSSStyleDeclaration)} userCallback
+     */
+    getComputedStyleAsync: function(nodeId, userCallback)
+    {
+        /**
+         * @param {function(?WebInspector.CSSStyleDeclaration)} userCallback
+         */
+        function callback(userCallback, error, computedPayload)
+        {
+            if (error || !computedPayload)
+                userCallback(null);
+            else
+                userCallback(WebInspector.CSSStyleDeclaration.parseComputedStylePayload(computedPayload));
+        }
+
+        CSSAgent.getComputedStyleForNode(nodeId, callback.bind(null, userCallback));
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {function(?WebInspector.CSSStyleDeclaration, ?WebInspector.CSSStyleDeclaration)} userCallback
+     */
+    getInlineStylesAsync: function(nodeId, userCallback)
+    {
+        /**
+         * @param {function(?WebInspector.CSSStyleDeclaration, ?WebInspector.CSSStyleDeclaration)} userCallback
+         * @param {?Protocol.Error} error
+         * @param {?CSSAgent.CSSStyle=} inlinePayload
+         * @param {?CSSAgent.CSSStyle=} attributesStylePayload
+         */
+        function callback(userCallback, error, inlinePayload, attributesStylePayload)
+        {
+            if (error || !inlinePayload)
+                userCallback(null, null);
+            else
+                userCallback(WebInspector.CSSStyleDeclaration.parsePayload(inlinePayload), attributesStylePayload ? WebInspector.CSSStyleDeclaration.parsePayload(attributesStylePayload) : null);
+        }
+
+        CSSAgent.getInlineStylesForNode(nodeId, callback.bind(null, userCallback));
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {?Array.<string>|undefined} forcedPseudoClasses
+     * @param {function()=} userCallback
+     */
+    forcePseudoState: function(nodeId, forcedPseudoClasses, userCallback)
+    {
+        CSSAgent.forcePseudoState(nodeId, forcedPseudoClasses || [], userCallback);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} documentNodeId
+     * @param {function(?WebInspector.NamedFlowCollection)} userCallback
+     */
+    getNamedFlowCollectionAsync: function(documentNodeId, userCallback)
+    {
+        var namedFlowCollection = this._namedFlowCollections[documentNodeId];
+        if (namedFlowCollection) {
+            userCallback(namedFlowCollection);
+            return;
+        }
+
+        /**
+         * @param {function(?WebInspector.NamedFlowCollection)} userCallback
+         * @param {?Protocol.Error} error
+         * @param {?Array.<CSSAgent.NamedFlow>} namedFlowPayload
+         */
+        function callback(userCallback, error, namedFlowPayload)
+        {
+            if (error || !namedFlowPayload)
+                userCallback(null);
+            else {
+                var namedFlowCollection = new WebInspector.NamedFlowCollection(namedFlowPayload);
+                this._namedFlowCollections[documentNodeId] = namedFlowCollection;
+                userCallback(namedFlowCollection);
+            }
+        }
+
+        CSSAgent.getNamedFlowCollection(documentNodeId, callback.bind(this, userCallback));
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} documentNodeId
+     * @param {string} flowName
+     * @param {function(?WebInspector.NamedFlow)} userCallback
+     */
+    getFlowByNameAsync: function(documentNodeId, flowName, userCallback)
+    {
+        var namedFlowCollection = this._namedFlowCollections[documentNodeId];
+        if (namedFlowCollection) {
+            userCallback(namedFlowCollection.flowByName(flowName));
+            return;
+        }
+
+        /**
+         * @param {function(?WebInspector.NamedFlow)} userCallback
+         * @param {?WebInspector.NamedFlowCollection} namedFlowCollection
+         */
+        function callback(userCallback, namedFlowCollection)
+        {
+            if (!namedFlowCollection)
+                userCallback(null);
+            else
+                userCallback(namedFlowCollection.flowByName(flowName));
+        }
+
+        this.getNamedFlowCollectionAsync(documentNodeId, callback.bind(this, userCallback));
+    },
+
+    /**
+     * @param {CSSAgent.CSSRuleId} ruleId
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} newSelector
+     * @param {function(WebInspector.CSSRule, boolean)} successCallback
+     * @param {function()} failureCallback
+     */
+    setRuleSelector: function(ruleId, nodeId, newSelector, successCallback, failureCallback)
+    {
+        /**
+         * @param {DOMAgent.NodeId} nodeId
+         * @param {function(WebInspector.CSSRule, boolean)} successCallback
+         * @param {CSSAgent.CSSRule} rulePayload
+         * @param {?Array.<DOMAgent.NodeId>} selectedNodeIds
+         */
+        function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds)
+        {
+            if (!selectedNodeIds)
+                return;
+            var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0);
+            var rule = WebInspector.CSSRule.parsePayload(rulePayload);
+            successCallback(rule, doesAffectSelectedNode);
+        }
+
+        /**
+         * @param {DOMAgent.NodeId} nodeId
+         * @param {function(WebInspector.CSSRule, boolean)} successCallback
+         * @param {function()} failureCallback
+         * @param {?Protocol.Error} error
+         * @param {string} newSelector
+         * @param {?CSSAgent.CSSRule} rulePayload
+         */
+        function callback(nodeId, successCallback, failureCallback, newSelector, error, rulePayload)
+        {
+            this._pendingCommandsMajorState.pop();
+            if (error)
+                failureCallback();
+            else {
+                WebInspector.domAgent.markUndoableState();
+                var ownerDocumentId = this._ownerDocumentId(nodeId);
+                if (ownerDocumentId)
+                    WebInspector.domAgent.querySelectorAll(ownerDocumentId, newSelector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload));
+                else
+                    failureCallback();
+            }
+        }
+
+        this._pendingCommandsMajorState.push(true);
+        CSSAgent.setRuleSelector(ruleId, newSelector, callback.bind(this, nodeId, successCallback, failureCallback, newSelector));
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} selector
+     * @param {function(WebInspector.CSSRule, boolean)} successCallback
+     * @param {function()} failureCallback
+     */
+    addRule: function(nodeId, selector, successCallback, failureCallback)
+    {
+        /**
+         * @param {DOMAgent.NodeId} nodeId
+         * @param {function(WebInspector.CSSRule, boolean)} successCallback
+         * @param {CSSAgent.CSSRule} rulePayload
+         * @param {?Array.<DOMAgent.NodeId>} selectedNodeIds
+         */
+        function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds)
+        {
+            if (!selectedNodeIds)
+                return;
+
+            var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0);
+            var rule = WebInspector.CSSRule.parsePayload(rulePayload);
+            successCallback(rule, doesAffectSelectedNode);
+        }
+
+        /**
+         * @param {function(WebInspector.CSSRule, boolean)} successCallback
+         * @param {function()} failureCallback
+         * @param {string} selector
+         * @param {?Protocol.Error} error
+         * @param {?CSSAgent.CSSRule} rulePayload
+         */
+        function callback(successCallback, failureCallback, selector, error, rulePayload)
+        {
+            this._pendingCommandsMajorState.pop();
+            if (error) {
+                // Invalid syntax for a selector
+                failureCallback();
+            } else {
+                WebInspector.domAgent.markUndoableState();
+                var ownerDocumentId = this._ownerDocumentId(nodeId);
+                if (ownerDocumentId)
+                    WebInspector.domAgent.querySelectorAll(ownerDocumentId, selector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload));
+                else
+                    failureCallback();
+            }
+        }
+
+        this._pendingCommandsMajorState.push(true);
+        CSSAgent.addRule(nodeId, selector, callback.bind(this, successCallback, failureCallback, selector));
+    },
+
+    mediaQueryResultChanged: function()
+    {
+        this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged);
+    },
+
+    /**
+     * @return {Array.<WebInspector.CSSStyleSheetHeader>}
+     */
+    styleSheetHeaders: function()
+    {
+        return Object.values(this._resourceBinding._styleSheetIdToHeader);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     */
+    _ownerDocumentId: function(nodeId)
+    {
+        var node = WebInspector.domAgent.nodeForId(nodeId);
+        if (!node)
+            return null;
+        return node.ownerDocument ? node.ownerDocument.id : null;
+    },
+
+    /**
+     * @param {CSSAgent.StyleSheetId} styleSheetId
+     */
+    _fireStyleSheetChanged: function(styleSheetId)
+    {
+        if (!this._pendingCommandsMajorState.length)
+            return;
+
+        var majorChange = this._pendingCommandsMajorState[this._pendingCommandsMajorState.length - 1];
+
+        if (!majorChange || !styleSheetId || !this.hasEventListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged))
+            return;
+
+        this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged, { styleSheetId: styleSheetId, majorChange: majorChange });
+    },
+
+    /**
+     * @param {CSSAgent.CSSStyleSheetHeader} header
+     */
+    _styleSheetAdded: function(header)
+    {
+        this._resourceBinding._setHeaderForStyleSheetId(header.styleSheetId, header);
+        this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetAdded, header);
+    },
+
+    /**
+     * @param {!CSSAgent.StyleSheetId} id
+     */
+    _styleSheetRemoved: function(id)
+    {
+        var header = this._resourceBinding._styleSheetIdToHeader[id];
+        console.assert(header);
+        this._resourceBinding._setHeaderForStyleSheetId(id, null);
+        this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, header);
+    },
+
+    /**
+     * @param {CSSAgent.NamedFlow} namedFlowPayload
+     */
+    _namedFlowCreated: function(namedFlowPayload)
+    {
+        var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload);
+        var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId];
+
+        if (!namedFlowCollection)
+            return;
+
+        namedFlowCollection._appendNamedFlow(namedFlow);
+        this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.NamedFlowCreated, namedFlow);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} documentNodeId
+     * @param {string} flowName
+     */
+    _namedFlowRemoved: function(documentNodeId, flowName)
+    {
+        var namedFlowCollection = this._namedFlowCollections[documentNodeId];
+
+        if (!namedFlowCollection)
+            return;
+
+        namedFlowCollection._removeNamedFlow(flowName);
+        this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, { documentNodeId: documentNodeId, flowName: flowName });
+    },
+
+    /**
+     * @param {CSSAgent.NamedFlow} namedFlowPayload
+     */
+    _regionLayoutUpdated: function(namedFlowPayload)
+    {
+        var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload);
+        var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId];
+
+        if (!namedFlowCollection)
+            return;
+
+        namedFlowCollection._appendNamedFlow(namedFlow);
+        this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, namedFlow);
+    },
+
+    /**
+     * @param {CSSAgent.StyleSheetId} styleSheetId
+     * @param {string} newText
+     * @param {boolean} majorChange
+     * @param {function(?string)} userCallback
+     */
+    setStyleSheetText: function(styleSheetId, newText, majorChange, userCallback)
+    {
+        function callback(error)
+        {
+            this._pendingCommandsMajorState.pop();
+            if (!error && majorChange)
+                WebInspector.domAgent.markUndoableState();
+            
+            if (!error && userCallback)
+                userCallback(error);
+        }
+        this._pendingCommandsMajorState.push(majorChange);
+        CSSAgent.setStyleSheetText(styleSheetId, newText, callback.bind(this));
+    },
+
+    _undoRedoRequested: function()
+    {
+        this._pendingCommandsMajorState.push(true);
+    },
+
+    _undoRedoCompleted: function()
+    {
+        this._pendingCommandsMajorState.pop();
+    },
+
+    /**
+     * @param {WebInspector.CSSRule} rule
+     * @return {?WebInspector.Resource}
+     */
+    viaInspectorResourceForRule: function(rule)
+    {
+        if (!rule.id)
+            return null;
+        return this._resourceBinding._inspectorResource(rule.id.styleSheetId);
+    },
+
+    /**
+     * @return {WebInspector.CSSStyleModelResourceBinding}
+     */
+    resourceBinding: function()
+    {
+        return this._resourceBinding;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _mainFrameCreatedOrNavigated: function(event)
+    {
+        this._resetSourceMappings();
+        this._resourceBinding._reset();
+    },
+
+    /**
+     * @param {string} url
+     * @param {WebInspector.SourceMapping} sourceMapping
+     */
+    setSourceMapping: function(url, sourceMapping)
+    {
+        if (sourceMapping)
+            this._sourceMappings[url] = sourceMapping;
+        else
+            delete this._sourceMappings[url];
+        this._updateLocations();
+    },
+
+    _resetSourceMappings: function()
+    {
+        this._sourceMappings = {};
+    },
+
+    _resetNamedFlowCollections: function()
+    {
+        this._namedFlowCollections = {};
+    },
+
+    _updateLocations: function()
+    {
+        for (var i = 0; i < this._locations.length; ++i)
+            this._locations[i].update();
+    },
+
+    /**
+     * @param {WebInspector.CSSRule} cssRule
+     * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
+     * @return {?WebInspector.LiveLocation}
+     */
+    createLiveLocation: function(cssRule, updateDelegate)
+    {
+        if (!cssRule._rawLocation)
+            return null;
+        var location = new WebInspector.CSSStyleModel.LiveLocation(cssRule._rawLocation, updateDelegate);
+        if (!location.uiLocation())
+            return null;
+        this._locations.push(location);
+        location.update();
+        return location;
+    },
+
+    /**
+     * @param {WebInspector.CSSLocation} rawLocation
+     * @return {?WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(rawLocation)
+    {
+        var sourceMapping = this._sourceMappings[rawLocation.url];
+        if (sourceMapping) {
+            var uiLocation = sourceMapping.rawLocationToUILocation(rawLocation);
+            if (uiLocation)
+                return uiLocation;
+        }
+        var uiSourceCode = this._workspace.uiSourceCodeForURL(rawLocation.url);
+        if (!uiSourceCode)
+            return null;
+        return new WebInspector.UILocation(uiSourceCode, rawLocation.lineNumber, rawLocation.columnNumber);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.LiveLocation}
+ * @param {WebInspector.CSSLocation} rawLocation
+ * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
+ */
+WebInspector.CSSStyleModel.LiveLocation = function(rawLocation, updateDelegate)
+{
+    WebInspector.LiveLocation.call(this, rawLocation, updateDelegate);
+}
+
+WebInspector.CSSStyleModel.LiveLocation.prototype = {
+    /**
+     * @return {WebInspector.UILocation}
+     */
+    uiLocation: function()
+    {
+        var cssLocation = /** @type WebInspector.CSSLocation */ (this.rawLocation());
+        return WebInspector.cssModel.rawLocationToUILocation(cssLocation);
+    },
+
+    dispose: function()
+    {
+        WebInspector.LiveLocation.prototype.dispose.call(this);
+        var locations = WebInspector.cssModel._locations;
+        if (locations)
+            locations.remove(this);
+    },
+
+    __proto__: WebInspector.LiveLocation.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.RawLocation}
+ * @param {string} url
+ * @param {number} lineNumber
+ * @param {number=} columnNumber
+ */
+WebInspector.CSSLocation = function(url, lineNumber, columnNumber)
+{
+    this.url = url;
+    this.lineNumber = lineNumber;
+    this.columnNumber = columnNumber || 0;
+}
+
+/**
+ * @constructor
+ * @param {CSSAgent.CSSStyle} payload
+ */
+WebInspector.CSSStyleDeclaration = function(payload)
+{
+    this.id = payload.styleId;
+    this.width = payload.width;
+    this.height = payload.height;
+    this.range = payload.range;
+    this._shorthandValues = WebInspector.CSSStyleDeclaration.buildShorthandValueMap(payload.shorthandEntries);
+    this._livePropertyMap = {}; // LIVE properties (source-based or style-based) : { name -> CSSProperty }
+    this._allProperties = []; // ALL properties: [ CSSProperty ]
+    this.__disabledProperties = {}; // DISABLED properties: { index -> CSSProperty }
+    var payloadPropertyCount = payload.cssProperties.length;
+
+    var propertyIndex = 0;
+    for (var i = 0; i < payloadPropertyCount; ++i) {
+        var property = WebInspector.CSSProperty.parsePayload(this, i, payload.cssProperties[i]);
+        this._allProperties.push(property);
+        if (property.disabled)
+            this.__disabledProperties[i] = property;
+        if (!property.active && !property.styleBased)
+            continue;
+        var name = property.name;
+        this[propertyIndex] = name;
+        this._livePropertyMap[name] = property;
+        ++propertyIndex;
+    }
+    this.length = propertyIndex;
+    if ("cssText" in payload)
+        this.cssText = payload.cssText;
+}
+
+/**
+ * @param {Array.<CSSAgent.ShorthandEntry>} shorthandEntries
+ * @return {Object}
+ */
+WebInspector.CSSStyleDeclaration.buildShorthandValueMap = function(shorthandEntries)
+{
+    var result = {};
+    for (var i = 0; i < shorthandEntries.length; ++i)
+        result[shorthandEntries[i].name] = shorthandEntries[i].value;
+    return result;
+}
+
+/**
+ * @param {CSSAgent.CSSStyle} payload
+ * @return {WebInspector.CSSStyleDeclaration}
+ */
+WebInspector.CSSStyleDeclaration.parsePayload = function(payload)
+{
+    return new WebInspector.CSSStyleDeclaration(payload);
+}
+
+/**
+ * @param {Array.<CSSAgent.CSSComputedStyleProperty>} payload
+ * @return {WebInspector.CSSStyleDeclaration}
+ */
+WebInspector.CSSStyleDeclaration.parseComputedStylePayload = function(payload)
+{
+    var newPayload = /** @type {CSSAgent.CSSStyle} */ ({ cssProperties: [], shorthandEntries: [], width: "", height: "" });
+    if (payload)
+        newPayload.cssProperties = payload;
+
+    return new WebInspector.CSSStyleDeclaration(newPayload);
+}
+
+WebInspector.CSSStyleDeclaration.prototype = {
+    get allProperties()
+    {
+        return this._allProperties;
+    },
+
+    /**
+     * @param {string} name
+     * @return {WebInspector.CSSProperty|undefined}
+     */
+    getLiveProperty: function(name)
+    {
+        return this._livePropertyMap[name];
+    },
+
+    /**
+     * @param {string} name
+     * @return {string}
+     */
+    getPropertyValue: function(name)
+    {
+        var property = this._livePropertyMap[name];
+        return property ? property.value : "";
+    },
+
+    /**
+     * @param {string} name
+     * @return {string}
+     */
+    getPropertyPriority: function(name)
+    {
+        var property = this._livePropertyMap[name];
+        return property ? property.priority : "";
+    },
+
+    /**
+     * @param {string} name
+     * @return {boolean}
+     */
+    isPropertyImplicit: function(name)
+    {
+        var property = this._livePropertyMap[name];
+        return property ? property.implicit : "";
+    },
+
+    /**
+     * @param {string} name
+     * @return {Array.<WebInspector.CSSProperty>}
+     */
+    longhandProperties: function(name)
+    {
+        var longhands = WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(name);
+        var result = [];
+        for (var i = 0; longhands && i < longhands.length; ++i) {
+            var property = this._livePropertyMap[longhands[i]];
+            if (property)
+                result.push(property);
+        }
+        return result;
+    },
+
+    /**
+     * @param {string} shorthandProperty
+     * @return {string}
+     */
+    shorthandValue: function(shorthandProperty)
+    {
+        return this._shorthandValues[shorthandProperty];
+    },
+
+    /**
+     * @param {number} index
+     * @return {?WebInspector.CSSProperty}
+     */
+    propertyAt: function(index)
+    {
+        return (index < this.allProperties.length) ? this.allProperties[index] : null;
+    },
+
+    /**
+     * @return {number}
+     */
+    pastLastSourcePropertyIndex: function()
+    {
+        for (var i = this.allProperties.length - 1; i >= 0; --i) {
+            var property = this.allProperties[i];
+            if (property.active || property.disabled)
+                return i + 1;
+        }
+        return 0;
+    },
+
+    /**
+     * @param {number=} index
+     */
+    newBlankProperty: function(index)
+    {
+        index = (typeof index === "undefined") ? this.pastLastSourcePropertyIndex() : index;
+        return new WebInspector.CSSProperty(this, index, "", "", "", "active", true, false, "");
+    },
+
+    /**
+     * @param {number} index
+     * @param {string} name
+     * @param {string} value
+     * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
+     */
+    insertPropertyAt: function(index, name, value, userCallback)
+    {
+        /**
+         * @param {?string} error
+         * @param {CSSAgent.CSSStyle} payload
+         */
+        function callback(error, payload)
+        {
+            WebInspector.cssModel._pendingCommandsMajorState.pop();
+            if (!userCallback)
+                return;
+
+            if (error) {
+                console.error(error);
+                userCallback(null);
+            } else
+                userCallback(WebInspector.CSSStyleDeclaration.parsePayload(payload));
+        }
+
+        if (!this.id)
+            throw "No style id";
+
+        WebInspector.cssModel._pendingCommandsMajorState.push(true);
+        CSSAgent.setPropertyText(this.id, index, name + ": " + value + ";", false, callback.bind(this));
+    },
+
+    /**
+     * @param {string} name
+     * @param {string} value
+     * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
+     */
+    appendProperty: function(name, value, userCallback)
+    {
+        this.insertPropertyAt(this.allProperties.length, name, value, userCallback);
+    },
+
+    /**
+     * @param {string} text
+     * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
+     */
+    setText: function(text, userCallback)
+    {
+        /**
+         * @param {?string} error
+         * @param {CSSAgent.CSSStyle} payload
+         */
+        function callback(error, payload)
+        {
+            WebInspector.cssModel._pendingCommandsMajorState.pop();
+            if (!userCallback)
+                return;
+
+            if (error) {
+                console.error(error);
+                userCallback(null);
+            } else
+                userCallback(WebInspector.CSSStyleDeclaration.parsePayload(payload));
+        }
+
+        if (!this.id)
+            throw "No style id";
+
+        if (typeof this.cssText === "undefined") {
+            userCallback(null);
+            return;
+        }
+
+        WebInspector.cssModel._pendingCommandsMajorState.push(true);
+        CSSAgent.setStyleText(this.id, text, callback);
+    }
+}
+
+/**
+ * @constructor
+ * @param {CSSAgent.CSSRule} payload
+ * @param {Array.<number>=} matchingSelectors
+ */
+WebInspector.CSSRule = function(payload, matchingSelectors)
+{
+    this.id = payload.ruleId;
+    if (matchingSelectors)
+        this.matchingSelectors = matchingSelectors;
+    this.selectors = payload.selectorList.selectors;
+    this.selectorText = this.selectors.join(", ");
+    this.selectorRange = payload.selectorList.range;
+    this.sourceLine = payload.sourceLine;
+    this.sourceURL = payload.sourceURL;
+    this.origin = payload.origin;
+    this.style = WebInspector.CSSStyleDeclaration.parsePayload(payload.style);
+    this.style.parentRule = this;
+    if (payload.media)
+        this.media = WebInspector.CSSMedia.parseMediaArrayPayload(payload.media);
+    this._setRawLocation(payload);
+}
+
+/**
+ * @param {CSSAgent.CSSRule} payload
+ * @param {Array.<number>=} matchingIndices
+ * @return {WebInspector.CSSRule}
+ */
+WebInspector.CSSRule.parsePayload = function(payload, matchingIndices)
+{
+    return new WebInspector.CSSRule(payload, matchingIndices);
+}
+
+WebInspector.CSSRule.prototype = {
+    _setRawLocation: function(payload)
+    {
+        if (!payload.sourceURL)
+            return;
+        if (this.selectorRange) {
+            var resource = WebInspector.resourceTreeModel.resourceForURL(payload.sourceURL);
+            if (resource && resource.type === WebInspector.resourceTypes.Stylesheet) {
+                this._rawLocation = new WebInspector.CSSLocation(payload.sourceURL, this.selectorRange.startLine, this.selectorRange.startColumn);
+                return;
+            }
+        }
+        this._rawLocation = new WebInspector.CSSLocation(payload.sourceURL, payload.sourceLine);
+    },
+
+    get isUserAgent()
+    {
+        return this.origin === "user-agent";
+    },
+
+    get isUser()
+    {
+        return this.origin === "user";
+    },
+
+    get isViaInspector()
+    {
+        return this.origin === "inspector";
+    },
+
+    get isRegular()
+    {
+        return this.origin === "regular";
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isSourceNavigable: function()
+    {
+        if (!this.sourceURL)
+            return false;
+        var resource = WebInspector.resourceTreeModel.resourceForURL(this.sourceURL);
+        return !!resource && resource.contentType() === WebInspector.resourceTypes.Stylesheet;
+    }
+}
+
+/**
+ * @constructor
+ * @param {?WebInspector.CSSStyleDeclaration} ownerStyle
+ * @param {number} index
+ * @param {string} name
+ * @param {string} value
+ * @param {?string} priority
+ * @param {string} status
+ * @param {boolean} parsedOk
+ * @param {boolean} implicit
+ * @param {?string=} text
+ * @param {CSSAgent.SourceRange=} range
+ */
+WebInspector.CSSProperty = function(ownerStyle, index, name, value, priority, status, parsedOk, implicit, text, range)
+{
+    this.ownerStyle = ownerStyle;
+    this.index = index;
+    this.name = name;
+    this.value = value;
+    this.priority = priority;
+    this.status = status;
+    this.parsedOk = parsedOk;
+    this.implicit = implicit;
+    this.text = text;
+    this.range = range;
+}
+
+/**
+ * @param {?WebInspector.CSSStyleDeclaration} ownerStyle
+ * @param {number} index
+ * @param {CSSAgent.CSSProperty} payload
+ * @return {WebInspector.CSSProperty}
+ */
+WebInspector.CSSProperty.parsePayload = function(ownerStyle, index, payload)
+{
+    // The following default field values are used in the payload:
+    // priority: ""
+    // parsedOk: true
+    // implicit: false
+    // status: "style"
+    var result = new WebInspector.CSSProperty(
+        ownerStyle, index, payload.name, payload.value, payload.priority || "", payload.status || "style", ("parsedOk" in payload) ? !!payload.parsedOk : true, !!payload.implicit, payload.text, payload.range);
+    return result;
+}
+
+WebInspector.CSSProperty.prototype = {
+    get propertyText()
+    {
+        if (this.text !== undefined)
+            return this.text;
+
+        if (this.name === "")
+            return "";
+        return this.name + ": " + this.value + (this.priority ? " !" + this.priority : "") + ";";
+    },
+
+    get isLive()
+    {
+        return this.active || this.styleBased;
+    },
+
+    get active()
+    {
+        return this.status === "active";
+    },
+
+    get styleBased()
+    {
+        return this.status === "style";
+    },
+
+    get inactive()
+    {
+        return this.status === "inactive";
+    },
+
+    get disabled()
+    {
+        return this.status === "disabled";
+    },
+
+    /**
+     * Replaces "propertyName: propertyValue [!important];" in the stylesheet by an arbitrary propertyText.
+     *
+     * @param {string} propertyText
+     * @param {boolean} majorChange
+     * @param {boolean} overwrite
+     * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
+     */
+    setText: function(propertyText, majorChange, overwrite, userCallback)
+    {
+        /**
+         * @param {?WebInspector.CSSStyleDeclaration} style
+         */
+        function enabledCallback(style)
+        {
+            if (userCallback)
+                userCallback(style);
+        }
+
+        /**
+         * @param {?string} error
+         * @param {?CSSAgent.CSSStyle} stylePayload
+         */
+        function callback(error, stylePayload)
+        {
+            WebInspector.cssModel._pendingCommandsMajorState.pop();
+            if (!error) {
+                if (majorChange)
+                    WebInspector.domAgent.markUndoableState();
+                this.text = propertyText;
+                var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload);
+                var newProperty = style.allProperties[this.index];
+
+                if (newProperty && this.disabled && !propertyText.match(/^\s*$/)) {
+                    newProperty.setDisabled(false, enabledCallback);
+                    return;
+                }
+
+                if (userCallback)
+                    userCallback(style);
+            } else {
+                if (userCallback)
+                    userCallback(null);
+            }
+        }
+
+        if (!this.ownerStyle)
+            throw "No ownerStyle for property";
+
+        if (!this.ownerStyle.id)
+            throw "No owner style id";
+
+        // An index past all the properties adds a new property to the style.
+        WebInspector.cssModel._pendingCommandsMajorState.push(majorChange);
+        CSSAgent.setPropertyText(this.ownerStyle.id, this.index, propertyText, overwrite, callback.bind(this));
+    },
+
+    /**
+     * @param {string} newValue
+     * @param {boolean} majorChange
+     * @param {boolean} overwrite
+     * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
+     */
+    setValue: function(newValue, majorChange, overwrite, userCallback)
+    {
+        var text = this.name + ": " + newValue + (this.priority ? " !" + this.priority : "") + ";"
+        this.setText(text, majorChange, overwrite, userCallback);
+    },
+
+    /**
+     * @param {boolean} disabled
+     * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
+     */
+    setDisabled: function(disabled, userCallback)
+    {
+        if (!this.ownerStyle && userCallback)
+            userCallback(null);
+        if (disabled === this.disabled && userCallback)
+            userCallback(this.ownerStyle);
+
+        /**
+         * @param {?string} error
+         * @param {CSSAgent.CSSStyle} stylePayload
+         */
+        function callback(error, stylePayload)
+        {
+            WebInspector.cssModel._pendingCommandsMajorState.pop();
+            if (error) {
+                if (userCallback)
+                    userCallback(null);
+                return;
+            }
+            WebInspector.domAgent.markUndoableState();
+            if (userCallback) {
+                var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload);
+                userCallback(style);
+            }
+        }
+
+        if (!this.ownerStyle.id)
+            throw "No owner style id";
+
+        WebInspector.cssModel._pendingCommandsMajorState.push(false);
+        CSSAgent.toggleProperty(this.ownerStyle.id, this.index, disabled, callback.bind(this));
+    },
+
+    /**
+     * @param {boolean} forName
+     * @return {WebInspector.UILocation}
+     */
+    uiLocation: function(forName)
+    {
+        if (!this.range || !this.ownerStyle || !this.ownerStyle.parentRule || !this.ownerStyle.parentRule.sourceURL)
+            return null;
+
+        var range = this.range;
+        var line = forName ? range.startLine : range.endLine;
+        // End of range is exclusive, so subtract 1 from the end offset.
+        var column = forName ? range.startColumn : range.endColumn - 1;
+        var rawLocation = new WebInspector.CSSLocation(this.ownerStyle.parentRule.sourceURL, line, column);
+        return WebInspector.cssModel.rawLocationToUILocation(rawLocation);
+    }
+}
+
+/**
+ * @constructor
+ * @param {CSSAgent.CSSMedia} payload
+ */
+WebInspector.CSSMedia = function(payload)
+{
+    this.text = payload.text;
+    this.source = payload.source;
+    this.sourceURL = payload.sourceURL || "";
+    this.sourceLine = typeof payload.sourceLine === "undefined" || this.source === "linkedSheet" ? -1 : payload.sourceLine;
+}
+
+WebInspector.CSSMedia.Source = {
+    LINKED_SHEET: "linkedSheet",
+    INLINE_SHEET: "inlineSheet",
+    MEDIA_RULE: "mediaRule",
+    IMPORT_RULE: "importRule"
+};
+
+/**
+ * @param {CSSAgent.CSSMedia} payload
+ * @return {WebInspector.CSSMedia}
+ */
+WebInspector.CSSMedia.parsePayload = function(payload)
+{
+    return new WebInspector.CSSMedia(payload);
+}
+
+/**
+ * @param {Array.<CSSAgent.CSSMedia>} payload
+ * @return {Array.<WebInspector.CSSMedia>}
+ */
+WebInspector.CSSMedia.parseMediaArrayPayload = function(payload)
+{
+    var result = [];
+    for (var i = 0; i < payload.length; ++i)
+        result.push(WebInspector.CSSMedia.parsePayload(payload[i]));
+    return result;
+}
+
+/**
+ * @constructor
+ * @param {CSSAgent.CSSStyleSheetHeader} payload
+ */
+WebInspector.CSSStyleSheetHeader = function(payload)
+{
+    this.id = payload.styleSheetId;
+    this.frameId = payload.frameId;
+    this.sourceURL = payload.sourceURL;
+    this.origin = payload.origin;
+    this.title = payload.title;
+    this.disabled = payload.disabled;
+}
+
+WebInspector.CSSStyleSheetHeader.prototype = {
+    /**
+     * @return {string}
+     */
+    resourceURL: function()
+    {
+        return this.origin === "inspector" ? this._viaInspectorResourceURL() : this.sourceURL;
+    },
+
+    /**
+     * @return {string}
+     */
+    _key: function()
+    {
+        return this.frameId + ":" + this.resourceURL();
+    },
+
+    /**
+     * @return {string}
+     */
+    _viaInspectorResourceURL: function()
+    {
+        var parsedURL = new WebInspector.ParsedURL(this.sourceURL);
+        var fakeURL = "inspector://" + parsedURL.host + parsedURL.folderPathComponents;
+        if (!fakeURL.endsWith("/"))
+            fakeURL += "/";
+        fakeURL += "inspector-stylesheet";
+        return fakeURL;
+    }
+}
+
+/**
+ * @constructor
+ * @param {CSSAgent.CSSStyleSheetBody} payload
+ */
+WebInspector.CSSStyleSheet = function(payload)
+{
+    this.id = payload.styleSheetId;
+    this.rules = [];
+    this.styles = {};
+    for (var i = 0; i < payload.rules.length; ++i) {
+        var rule = WebInspector.CSSRule.parsePayload(payload.rules[i]);
+        this.rules.push(rule);
+        if (rule.style)
+            this.styles[rule.style.id] = rule.style;
+    }
+    if ("text" in payload)
+        this._text = payload.text;
+}
+
+/**
+ * @param {CSSAgent.StyleSheetId} styleSheetId
+ * @param {function(?WebInspector.CSSStyleSheet)} userCallback
+ */
+WebInspector.CSSStyleSheet.createForId = function(styleSheetId, userCallback)
+{
+    /**
+     * @param {?string} error
+     * @param {CSSAgent.CSSStyleSheetBody} styleSheetPayload
+     */
+    function callback(error, styleSheetPayload)
+    {
+        if (error)
+            userCallback(null);
+        else
+            userCallback(new WebInspector.CSSStyleSheet(styleSheetPayload));
+    }
+    CSSAgent.getStyleSheet(styleSheetId, callback.bind(this));
+}
+
+WebInspector.CSSStyleSheet.prototype = {
+    /**
+     * @return {string|undefined}
+     */
+    getText: function()
+    {
+        return this._text;
+    },
+
+    /**
+     * @param {string} newText
+     * @param {boolean} majorChange
+     * @param {function(?string)=} userCallback
+     */
+    setText: function(newText, majorChange, userCallback)
+    {
+        /**
+         * @param {?string} error
+         */
+        function callback(error)
+        {
+            if (!error)
+                WebInspector.domAgent.markUndoableState();
+
+            WebInspector.cssModel._pendingCommandsMajorState.pop();
+            if (userCallback)
+                userCallback(error);
+        }
+
+        WebInspector.cssModel._pendingCommandsMajorState.push(majorChange);
+        CSSAgent.setStyleSheetText(this.id, newText, callback.bind(this));
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.CSSStyleModelResourceBinding = function()
+{
+    this._reset();
+}
+
+WebInspector.CSSStyleModelResourceBinding.prototype = {
+    /**
+     * @param {CSSAgent.StyleSheetId} styleSheetId
+     * @param {CSSAgent.CSSStyleSheetHeader} header
+     */
+    _setHeaderForStyleSheetId: function(styleSheetId, header)
+    {
+        var oldHeader = this._styleSheetIdToHeader[styleSheetId];
+        if (oldHeader) {
+            delete this._styleSheetIdToHeader[styleSheetId];
+            delete this._frameAndURLToStyleSheetId[oldHeader._key()];
+        }
+        if (header) {
+            var styleSheetHeader = new WebInspector.CSSStyleSheetHeader(header);
+            this._styleSheetIdToHeader[styleSheetId] = styleSheetHeader;
+            if (styleSheetHeader.origin === "inspector")
+                this._createInspectorResource(styleSheetHeader);
+            else
+                this._frameAndURLToStyleSheetId[styleSheetHeader._key()] = styleSheetHeader.id;
+        }
+    },
+
+    /**
+     * @param {CSSAgent.StyleSheetId} styleSheetId
+     * @return {?string}
+     */
+    resourceURLForStyleSheetId: function(styleSheetId)
+    {
+        var header = this._styleSheetIdToHeader[styleSheetId];
+        if (!header)
+            return null;
+
+        var frame = WebInspector.resourceTreeModel.frameForId(header.frameId);
+        if (!frame)
+            return null;
+
+        return header.resourceURL();
+    },
+
+    /**
+     * @param {WebInspector.Resource} resource
+     * @return {?CSSAgent.StyleSheetId}
+     */
+    styleSheetIdForResource: function(resource)
+    {
+        return this._frameAndURLToStyleSheetId[resource.frameId + ":" + resource.url] || null;
+    },
+
+    /**
+     * @param {WebInspector.CSSStyleSheetHeader} header
+     */
+    _createInspectorResource: function(header)
+    {
+        var frame = WebInspector.resourceTreeModel.frameForId(header.frameId);
+        if (!frame)
+            return;
+
+        var viaInspectorURL = header._viaInspectorResourceURL();
+        console.assert(!frame.resourceForURL(viaInspectorURL));
+
+        var resource = frame.resourceForURL(header.sourceURL);
+        if (!resource)
+            return;
+
+        this._frameAndURLToStyleSheetId[header._key()] = header.id;
+        var inspectorResource = new WebInspector.Resource(null, viaInspectorURL, resource.documentURL, resource.frameId, resource.loaderId, WebInspector.resourceTypes.Stylesheet, "text/css", true);
+
+        /**
+         * @param {function(?string, boolean, string)} callback
+         */
+        function overrideRequestContent(callback)
+        {
+            function callbackWrapper(error, content)
+            {
+                callback(error ? "" : content, false, "text/css");
+            }
+            CSSAgent.getStyleSheetText(header.id, callbackWrapper);
+        }
+        inspectorResource.requestContent = overrideRequestContent;
+        frame.addResource(inspectorResource);
+    },
+
+    /**
+     * @param {CSSAgent.StyleSheetId} styleSheetId
+     * @return {?WebInspector.Resource}
+     */
+    _inspectorResource: function(styleSheetId)
+    {
+        var header = this._styleSheetIdToHeader[styleSheetId];
+        if (!header)
+            return null;
+        var frame = WebInspector.resourceTreeModel.frameForId(header.frameId);
+        if (!frame)
+            return null;
+
+        var viaInspectorURL = header._viaInspectorResourceURL();
+        return frame.resourceForURL(viaInspectorURL);
+    },
+
+    _reset: function()
+    {
+        // Main frame navigation - clear history.
+        /** @type {!Object.<string, !CSSAgent.StyleSheetId>} */
+        this._frameAndURLToStyleSheetId = {};
+        /** @type {!Object.<CSSAgent.StyleSheetId, !WebInspector.CSSStyleSheetHeader>} */
+        this._styleSheetIdToHeader = {};
+    }
+}
+
+/**
+ * @constructor
+ * @implements {CSSAgent.Dispatcher}
+ * @param {WebInspector.CSSStyleModel} cssModel
+ */
+WebInspector.CSSDispatcher = function(cssModel)
+{
+    this._cssModel = cssModel;
+}
+
+WebInspector.CSSDispatcher.prototype = {
+    mediaQueryResultChanged: function()
+    {
+        this._cssModel.mediaQueryResultChanged();
+    },
+
+    /**
+     * @param {CSSAgent.StyleSheetId} styleSheetId
+     */
+    styleSheetChanged: function(styleSheetId)
+    {
+        this._cssModel._fireStyleSheetChanged(styleSheetId);
+    },
+
+    /**
+     * @param {CSSAgent.CSSStyleSheetHeader} header
+     */
+    styleSheetAdded: function(header)
+    {
+        this._cssModel._styleSheetAdded(header);
+    },
+
+    /**
+     * @param {CSSAgent.StyleSheetId} id
+     */
+    styleSheetRemoved: function(id)
+    {
+        this._cssModel._styleSheetRemoved(id);
+    },
+
+    /**
+     * @param {CSSAgent.NamedFlow} namedFlowPayload
+     */
+    namedFlowCreated: function(namedFlowPayload)
+    {
+        this._cssModel._namedFlowCreated(namedFlowPayload);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} documentNodeId
+     * @param {string} flowName
+     */
+    namedFlowRemoved: function(documentNodeId, flowName)
+    {
+        this._cssModel._namedFlowRemoved(documentNodeId, flowName);
+    },
+
+    /**
+     * @param {CSSAgent.NamedFlow} namedFlowPayload
+     */
+    regionLayoutUpdated: function(namedFlowPayload)
+    {
+        this._cssModel._regionLayoutUpdated(namedFlowPayload);
+    }
+}
+
+/**
+ * @constructor
+ * @param {CSSAgent.NamedFlow} payload
+ */
+WebInspector.NamedFlow = function(payload)
+{
+    this.documentNodeId = payload.documentNodeId;
+    this.name = payload.name;
+    this.overset = payload.overset;
+    this.content = payload.content;
+    this.regions = payload.regions;
+}
+
+/**
+ * @param {CSSAgent.NamedFlow} payload
+ * @return {WebInspector.NamedFlow}
+ */
+WebInspector.NamedFlow.parsePayload = function(payload)
+{
+    return new WebInspector.NamedFlow(payload);
+}
+
+/**
+ * @constructor
+ * @param {Array.<CSSAgent.NamedFlow>} payload
+ */
+WebInspector.NamedFlowCollection = function(payload)
+{
+    /** @type {Object.<string, WebInspector.NamedFlow>} */
+    this.namedFlowMap = {};
+
+    for (var i = 0; i < payload.length; ++i) {
+        var namedFlow = WebInspector.NamedFlow.parsePayload(payload[i]);
+        this.namedFlowMap[namedFlow.name] = namedFlow;
+    }
+}
+
+WebInspector.NamedFlowCollection.prototype = {
+    /**
+     * @param {WebInspector.NamedFlow} namedFlow
+     */
+    _appendNamedFlow: function(namedFlow)
+    {
+        this.namedFlowMap[namedFlow.name] = namedFlow;
+    },
+
+    /**
+     * @param {string} flowName
+     */
+    _removeNamedFlow: function(flowName)
+    {
+        delete this.namedFlowMap[flowName];
+    },
+
+    /**
+     * @param {string} flowName
+     * @return {WebInspector.NamedFlow}
+     */
+    flowByName: function(flowName)
+    {
+        var namedFlow = this.namedFlowMap[flowName];
+
+        if (!namedFlow)
+            return null;
+        return namedFlow;
+    }
+}
+/**
+ * @type {WebInspector.CSSStyleModel}
+ */
+WebInspector.cssModel = null;
diff --git a/Source/devtools/front_end/CallStackSidebarPane.js b/Source/devtools/front_end/CallStackSidebarPane.js
new file mode 100644
index 0000000..f90debc
--- /dev/null
+++ b/Source/devtools/front_end/CallStackSidebarPane.js
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.CallStackSidebarPane = function()
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Call Stack"));
+    this._model = WebInspector.debuggerModel;
+
+    this.bodyElement.addEventListener("keydown", this._keyDown.bind(this), true);
+    this.bodyElement.tabIndex = 0;
+}
+
+WebInspector.CallStackSidebarPane.prototype = {
+    update: function(callFrames)
+    {
+        this.bodyElement.removeChildren();
+        delete this._statusMessageElement;
+        this.placards = [];
+
+        if (!callFrames) {
+            var infoElement = document.createElement("div");
+            infoElement.className = "info";
+            infoElement.textContent = WebInspector.UIString("Not Paused");
+            this.bodyElement.appendChild(infoElement);
+            return;
+        }
+
+        for (var i = 0; i < callFrames.length; ++i) {
+            var callFrame = callFrames[i];
+            var placard = new WebInspector.CallStackSidebarPane.Placard(callFrame, this);
+            placard.element.addEventListener("click", this._placardSelected.bind(this, placard), false);
+            this.placards.push(placard);
+            this.bodyElement.appendChild(placard.element);
+        }
+    },
+
+    setSelectedCallFrame: function(x)
+    {
+        for (var i = 0; i < this.placards.length; ++i) {
+            var placard = this.placards[i];
+            placard.selected = (placard._callFrame === x);
+        }
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _selectNextCallFrameOnStack: function(event)
+    {
+        var index = this._selectedCallFrameIndex();
+        if (index == -1)
+            return true;
+        this._selectedPlacardByIndex(index + 1);
+        return true;
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _selectPreviousCallFrameOnStack: function(event)
+    {
+        var index = this._selectedCallFrameIndex();
+        if (index == -1)
+            return true;
+        this._selectedPlacardByIndex(index - 1);
+        return true;
+    },
+
+    /**
+     * @param {number} index
+     */
+    _selectedPlacardByIndex: function(index)
+    {
+        if (index < 0 || index >= this.placards.length)
+            return;
+        this._placardSelected(this.placards[index])
+    },
+
+    /**
+     * @return {number}
+     */
+    _selectedCallFrameIndex: function()
+    {
+        if (!this._model.selectedCallFrame())
+            return -1;
+        for (var i = 0; i < this.placards.length; ++i) {
+            var placard = this.placards[i];
+            if (placard._callFrame === this._model.selectedCallFrame())
+                return i;
+        }
+        return -1;
+    },
+
+    _placardSelected: function(placard)
+    {
+        this._model.setSelectedCallFrame(placard._callFrame);
+    },
+
+    _copyStackTrace: function()
+    {
+        var text = "";
+        for (var i = 0; i < this.placards.length; ++i)
+            text += this.placards[i].title + " (" + this.placards[i].subtitle + ")\n";
+        InspectorFrontendHost.copyText(text);
+    },
+
+    /**
+     * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(Event=):boolean)} registerShortcutDelegate
+     */
+    registerShortcuts: function(registerShortcutDelegate)
+    {
+        registerShortcutDelegate(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.NextCallFrame, this._selectNextCallFrameOnStack.bind(this));
+        registerShortcutDelegate(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.PrevCallFrame, this._selectPreviousCallFrameOnStack.bind(this));
+    },
+
+    setStatus: function(status)
+    {
+        if (!this._statusMessageElement) {
+            this._statusMessageElement = document.createElement("div");
+            this._statusMessageElement.className = "info";
+            this.bodyElement.appendChild(this._statusMessageElement);
+        }
+        if (typeof status === "string")
+            this._statusMessageElement.textContent = status;
+        else {
+            this._statusMessageElement.removeChildren();
+            this._statusMessageElement.appendChild(status);
+        }
+    },
+
+    _keyDown: function(event)
+    {
+        if (event.altKey || event.shiftKey || event.metaKey || event.ctrlKey)
+            return;
+
+        if (event.keyIdentifier === "Up") {
+            this._selectPreviousCallFrameOnStack();
+            event.consume();
+        } else if (event.keyIdentifier === "Down") {
+            this._selectNextCallFrameOnStack();
+            event.consume();
+        }
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Placard}
+ * @param {WebInspector.DebuggerModel.CallFrame} callFrame
+ * @param {WebInspector.CallStackSidebarPane} pane
+ */
+WebInspector.CallStackSidebarPane.Placard = function(callFrame, pane)
+{
+    WebInspector.Placard.call(this, callFrame.functionName || WebInspector.UIString("(anonymous function)"), "");
+    callFrame.createLiveLocation(this._update.bind(this));
+    this.element.addEventListener("contextmenu", this._placardContextMenu.bind(this), true);
+    this._callFrame = callFrame;
+    this._pane = pane;
+}
+
+WebInspector.CallStackSidebarPane.Placard.prototype = {
+    _update: function(uiLocation)
+    {
+        this.subtitle = uiLocation.linkText().trimMiddle(100);
+    },
+
+    _placardContextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+
+        if (WebInspector.debuggerModel.canSetScriptSource()) {
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Restart frame" : "Restart Frame"), this._restartFrame.bind(this));
+            contextMenu.appendSeparator();
+        }
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy stack trace" : "Copy Stack Trace"), this._pane._copyStackTrace.bind(this._pane));
+
+        contextMenu.show();
+    },
+
+    _restartFrame: function()
+    {
+        this._callFrame.restart(undefined);
+    },
+
+    __proto__: WebInspector.Placard.prototype
+}
diff --git a/Source/devtools/front_end/CanvasProfileView.js b/Source/devtools/front_end/CanvasProfileView.js
new file mode 100644
index 0000000..0a9ddeb
--- /dev/null
+++ b/Source/devtools/front_end/CanvasProfileView.js
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {!WebInspector.CanvasProfileHeader} profile
+ */
+WebInspector.CanvasProfileView = function(profile)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("canvasProfiler.css");
+    this._profile = profile;
+    this._traceLogId = profile.traceLogId();
+    this.element.addStyleClass("canvas-profile-view");
+
+    this._linkifier = new WebInspector.Linkifier();
+    this._splitView = new WebInspector.SplitView(false, "canvasProfileViewSplitLocation", 300);
+
+    var replayImageContainer = this._splitView.firstElement();
+    replayImageContainer.id = "canvas-replay-image-container";
+    this._replayImageElement = replayImageContainer.createChild("image", "canvas-replay-image");
+    this._debugInfoElement = replayImageContainer.createChild("div", "canvas-debug-info hidden");
+    this._spinnerIcon = replayImageContainer.createChild("img", "canvas-spinner-icon hidden");
+
+    var replayInfoContainer = this._splitView.secondElement();
+    var controlsContainer = replayInfoContainer.createChild("div", "status-bar");
+    var logGridContainer = replayInfoContainer.createChild("div", "canvas-replay-log");
+
+    this._createControlButton(controlsContainer, "canvas-replay-first-step", WebInspector.UIString("First call."), this._onReplayFirstStepClick.bind(this));
+    this._createControlButton(controlsContainer, "canvas-replay-prev-step", WebInspector.UIString("Previous call."), this._onReplayStepClick.bind(this, false));
+    this._createControlButton(controlsContainer, "canvas-replay-next-step", WebInspector.UIString("Next call."), this._onReplayStepClick.bind(this, true));
+    this._createControlButton(controlsContainer, "canvas-replay-prev-draw", WebInspector.UIString("Previous drawing call."), this._onReplayDrawingCallClick.bind(this, false));
+    this._createControlButton(controlsContainer, "canvas-replay-next-draw", WebInspector.UIString("Next drawing call."), this._onReplayDrawingCallClick.bind(this, true));
+    this._createControlButton(controlsContainer, "canvas-replay-last-step", WebInspector.UIString("Last call."), this._onReplayLastStepClick.bind(this));
+
+    this._replayContextSelector = new WebInspector.StatusBarComboBox(this._onReplayContextChanged.bind(this));
+    this._replayContextSelector.createOption("<screenshot auto>", WebInspector.UIString("Show screenshot of the last replayed resource."), "");
+    controlsContainer.appendChild(this._replayContextSelector.element);
+
+    /** @type {!Object.<string, boolean>} */
+    this._replayContexts = {};
+    /** @type {!Object.<string, CanvasAgent.ResourceState>} */
+    this._currentResourceStates = {};
+
+    var columns = [
+        {title: "#", sortable: true, width: "5%"},
+        {title: WebInspector.UIString("Call"), sortable: true, width: "75%", disclosure: true},
+        {title: WebInspector.UIString("Location"), sortable: true, width: "20%"}
+    ];
+
+    this._logGrid = new WebInspector.DataGrid(columns);
+    this._logGrid.element.addStyleClass("fill");
+    this._logGrid.show(logGridContainer);
+    this._logGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._replayTraceLog.bind(this));
+
+    this._splitView.show(this.element);
+    this._requestTraceLog(0);
+}
+
+/**
+ * @const
+ * @type {number}
+ */
+WebInspector.CanvasProfileView.TraceLogPollingInterval = 500;
+
+WebInspector.CanvasProfileView.prototype = {
+    dispose: function()
+    {
+        this._linkifier.reset();
+    },
+
+    get statusBarItems()
+    {
+        return [];
+    },
+
+    get profile()
+    {
+        return this._profile;
+    },
+
+    /**
+     * @override
+     * @return {Array.<Element>}
+     */
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return [this._logGrid.scrollContainer];
+    },
+
+    /**
+     * @param {Element} parent
+     * @param {string} className
+     * @param {string} title
+     * @param {function(this:WebInspector.CanvasProfileView)} clickCallback
+     */
+    _createControlButton: function(parent, className, title, clickCallback)
+    {
+        var button = new WebInspector.StatusBarButton(title, className);
+        parent.appendChild(button.element);
+
+        button.makeLongClickEnabled();
+        button.addEventListener("click", clickCallback, this);
+        button.addEventListener("longClickDown", clickCallback, this);
+        button.addEventListener("longClickPress", clickCallback, this);
+    },
+
+    _onReplayContextChanged: function()
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {CanvasAgent.ResourceState} resourceState
+         */
+        function didReceiveResourceState(error, resourceState)
+        {
+            this._enableWaitIcon(false);
+            if (error)
+                return;
+
+            this._currentResourceStates[resourceState.id] = resourceState;
+
+            var selectedContextId = this._replayContextSelector.selectedOption().value;
+            if (selectedContextId === resourceState.id)
+                this._replayImageElement.src = resourceState.imageURL;
+        }
+
+        var selectedContextId = this._replayContextSelector.selectedOption().value || "auto";
+        var resourceState = this._currentResourceStates[selectedContextId];
+        if (resourceState)
+            this._replayImageElement.src = resourceState.imageURL;
+        else {
+            this._enableWaitIcon(true);
+            this._replayImageElement.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; // Empty transparent image.
+            CanvasAgent.getResourceState(this._traceLogId, selectedContextId, didReceiveResourceState.bind(this));
+        }
+    },
+
+    /**
+     * @param {boolean} forward
+     */
+    _onReplayStepClick: function(forward)
+    {
+        var selectedNode = this._logGrid.selectedNode;
+        if (!selectedNode)
+            return;
+        var nextNode = selectedNode;
+        do {
+            nextNode = forward ? nextNode.traverseNextNode(false) : nextNode.traversePreviousNode(false);
+        } while (nextNode && typeof nextNode.index !== "number");
+        (nextNode || selectedNode).revealAndSelect();
+    },
+
+    /**
+     * @param {boolean} forward
+     */
+    _onReplayDrawingCallClick: function(forward)
+    {
+        var selectedNode = this._logGrid.selectedNode;
+        if (!selectedNode)
+            return;
+        var nextNode = selectedNode;
+        while (nextNode) {
+            var sibling = forward ? nextNode.nextSibling : nextNode.previousSibling;
+            if (sibling) {
+                nextNode = sibling;
+                if (nextNode.hasChildren || nextNode.call.isDrawingCall)
+                    break;
+            } else {
+                nextNode = nextNode.parent;
+                if (!forward)
+                    break;
+            }
+        }
+        if (!nextNode && forward)
+            this._onReplayLastStepClick();
+        else
+            (nextNode || selectedNode).revealAndSelect();
+    },
+
+    _onReplayFirstStepClick: function()
+    {
+        var firstNode = this._logGrid.rootNode().children[0];
+        if (firstNode)
+            firstNode.revealAndSelect();
+    },
+
+    _onReplayLastStepClick: function()
+    {
+        var lastNode = this._logGrid.rootNode().children.peekLast();
+        if (!lastNode)
+            return;
+        while (lastNode.expanded) {
+            var lastChild = lastNode.children.peekLast();
+            if (!lastChild)
+                break;
+            lastNode = lastChild;
+        }
+        lastNode.revealAndSelect();
+    },
+
+    /**
+     * @param {boolean} enable
+     */
+    _enableWaitIcon: function(enable)
+    {
+        this._spinnerIcon.enableStyleClass("hidden", !enable);
+        this._debugInfoElement.enableStyleClass("hidden", enable);
+    },
+
+    _replayTraceLog: function()
+    {
+        if (this._pendingReplayTraceLogEvent)
+            return;
+        var index = this._selectedCallIndex();
+        if (index === -1 || index === this._lastReplayCallIndex)
+            return;
+        this._lastReplayCallIndex = index;
+        this._pendingReplayTraceLogEvent = true;
+        var time = Date.now();
+        /**
+         * @param {?Protocol.Error} error
+         * @param {CanvasAgent.ResourceState} resourceState
+         */
+        function didReplayTraceLog(error, resourceState)
+        {
+            delete this._pendingReplayTraceLogEvent;
+
+            this._enableWaitIcon(false);
+
+            if (!error) {
+                this._currentResourceStates = {};
+                this._currentResourceStates["auto"] = resourceState;
+                this._currentResourceStates[resourceState.id] = resourceState;
+
+                this._debugInfoElement.textContent = "Replay time: " + (Date.now() - time) + "ms";
+                this._onReplayContextChanged();
+            }
+
+            if (index !== this._selectedCallIndex())
+                this._replayTraceLog();
+        }
+        this._enableWaitIcon(true);
+        CanvasAgent.replayTraceLog(this._traceLogId, index, didReplayTraceLog.bind(this));
+    },
+
+    /**
+     * @param {?Protocol.Error} error
+     * @param {CanvasAgent.TraceLog} traceLog
+     */
+    _didReceiveTraceLog: function(error, traceLog)
+    {
+        this._enableWaitIcon(false);
+        if (error || !traceLog)
+            return;
+        var callNodes = [];
+        var calls = traceLog.calls;
+        var index = traceLog.startOffset;
+        for (var i = 0, n = calls.length; i < n; ++i) {
+            var call = calls[i];
+            this._requestReplayContextInfo(call.contextId);
+            var gridNode = this._createCallNode(index++, call);
+            callNodes.push(gridNode);
+        }
+        this._appendCallNodes(callNodes);
+        if (traceLog.alive)
+            setTimeout(this._requestTraceLog.bind(this, index), WebInspector.CanvasProfileView.TraceLogPollingInterval);
+        else
+            this._flattenSingleFrameNode();
+        this._profile._updateCapturingStatus(traceLog);
+        this._onReplayLastStepClick(); // Automatically replay the last step.
+    },
+
+    /**
+     * @param {number} offset
+     */
+    _requestTraceLog: function(offset)
+    {
+        this._enableWaitIcon(true);
+        CanvasAgent.getTraceLog(this._traceLogId, offset, undefined, this._didReceiveTraceLog.bind(this));
+    },
+
+    /**
+     * @param {string} contextId
+     */
+    _requestReplayContextInfo: function(contextId)
+    {
+        if (this._replayContexts[contextId])
+            return;
+        this._replayContexts[contextId] = true;
+        /**
+         * @param {?Protocol.Error} error
+         * @param {CanvasAgent.ResourceInfo} resourceInfo
+         */
+        function didReceiveResourceInfo(error, resourceInfo)
+        {
+            if (error) {
+                delete this._replayContexts[contextId];
+                return;
+            }
+            this._replayContextSelector.createOption(resourceInfo.description, WebInspector.UIString("Show screenshot of this context's canvas."), contextId);
+        }
+        CanvasAgent.getResourceInfo(contextId, didReceiveResourceInfo.bind(this));
+    },
+
+    /**
+     * @return {number}
+     */
+    _selectedCallIndex: function()
+    {
+        var node = this._logGrid.selectedNode;
+        return node ? this._peekLastRecursively(node).index : -1;
+    },
+
+    /**
+     * @param {!WebInspector.DataGridNode} node
+     * @return {!WebInspector.DataGridNode}
+     */
+    _peekLastRecursively: function(node)
+    {
+        var lastChild;
+        while ((lastChild = node.children.peekLast()))
+            node = /** @type {!WebInspector.DataGridNode} */ (lastChild);
+        return node;
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.DataGridNode>} callNodes
+     */
+    _appendCallNodes: function(callNodes)
+    {
+        var rootNode = this._logGrid.rootNode();
+        var frameNode = /** @type {WebInspector.DataGridNode} */ (rootNode.children.peekLast());
+        if (frameNode && this._peekLastRecursively(frameNode).call.isFrameEndCall)
+            frameNode = null;
+        for (var i = 0, n = callNodes.length; i < n; ++i) {
+            if (!frameNode) {
+                var index = rootNode.children.length;
+                var data = {};
+                data[0] = "";
+                data[1] = "Frame #" + (index + 1);
+                data[2] = "";
+                frameNode = new WebInspector.DataGridNode(data);
+                frameNode.selectable = true;
+                rootNode.appendChild(frameNode);
+            }
+            var nextFrameCallIndex = i + 1;
+            while (nextFrameCallIndex < n && !callNodes[nextFrameCallIndex - 1].call.isFrameEndCall)
+                ++nextFrameCallIndex;
+            this._appendCallNodesToFrameNode(frameNode, callNodes, i, nextFrameCallIndex);
+            i = nextFrameCallIndex - 1;
+            frameNode = null;
+        }
+    },
+
+    /**
+     * @param {!WebInspector.DataGridNode} frameNode
+     * @param {!Array.<!WebInspector.DataGridNode>} callNodes
+     * @param {number} fromIndex
+     * @param {number} toIndex not inclusive
+     */
+    _appendCallNodesToFrameNode: function(frameNode, callNodes, fromIndex, toIndex)
+    {
+        var self = this;
+        function appendDrawCallGroup()
+        {
+            var index = self._drawCallGroupsCount || 0;
+            var data = {};
+            data[0] = "";
+            data[1] = "Draw call group #" + (index + 1);
+            data[2] = "";
+            var node = new WebInspector.DataGridNode(data);
+            node.selectable = true;
+            self._drawCallGroupsCount = index + 1;
+            frameNode.appendChild(node);
+            return node;
+        }
+
+        function splitDrawCallGroup(drawCallGroup)
+        {
+            var splitIndex = 0;
+            var splitNode;
+            while ((splitNode = drawCallGroup.children[splitIndex])) {
+                if (splitNode.call.isDrawingCall)
+                    break;
+                ++splitIndex;
+            }
+            var newDrawCallGroup = appendDrawCallGroup();
+            var lastNode;
+            while ((lastNode = drawCallGroup.children[splitIndex + 1]))
+                newDrawCallGroup.appendChild(lastNode);
+            return newDrawCallGroup;
+        }
+
+        var drawCallGroup = frameNode.children.peekLast();
+        var groupHasDrawCall = false;
+        if (drawCallGroup) {
+            for (var i = 0, n = drawCallGroup.children.length; i < n; ++i) {
+                if (drawCallGroup.children[i].call.isDrawingCall) {
+                    groupHasDrawCall = true;
+                    break;
+                }
+            }
+        } else
+            drawCallGroup = appendDrawCallGroup();
+
+        for (var i = fromIndex; i < toIndex; ++i) {
+            var node = callNodes[i];
+            drawCallGroup.appendChild(node);
+            if (node.call.isDrawingCall) {
+                if (groupHasDrawCall)
+                    drawCallGroup = splitDrawCallGroup(drawCallGroup);
+                else
+                    groupHasDrawCall = true;
+            }
+        }
+    },
+
+    /**
+     * @param {number} index
+     * @param {CanvasAgent.Call} call
+     * @return {!WebInspector.DataGridNode}
+     */
+    _createCallNode: function(index, call)
+    {
+        var data = {};
+        data[0] = index + 1;
+        data[1] = call.functionName || "context." + call.property;
+        data[2] = "";
+        if (call.sourceURL) {
+            // FIXME(62725): stack trace line/column numbers are one-based.
+            var lineNumber = Math.max(0, call.lineNumber - 1) || 0;
+            var columnNumber = Math.max(0, call.columnNumber - 1) || 0;
+            data[2] = this._linkifier.linkifyLocation(call.sourceURL, lineNumber, columnNumber);
+        }
+
+        if (call.arguments) {
+            var args = call.arguments.map(function(argument) {
+                return argument.description;
+            });
+            data[1] += "(" + args.join(", ") + ")";
+        } else
+            data[1] += " = " + call.value.description;
+
+        if (typeof call.result !== "undefined")
+            data[1] += " => " + call.result.description;
+
+        var node = new WebInspector.DataGridNode(data);
+        node.index = index;
+        node.selectable = true;
+        node.call = call;
+        return node;
+    },
+
+    _flattenSingleFrameNode: function()
+    {
+        var rootNode = this._logGrid.rootNode();
+        if (rootNode.children.length !== 1)
+            return;
+        var frameNode = rootNode.children[0];
+        while (frameNode.children[0])
+            rootNode.appendChild(frameNode.children[0]);
+        rootNode.removeChild(frameNode);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileType}
+ */
+WebInspector.CanvasProfileType = function()
+{
+    WebInspector.ProfileType.call(this, WebInspector.CanvasProfileType.TypeId, WebInspector.UIString("Capture Canvas Frame"));
+    this._nextProfileUid = 1;
+    this._recording = false;
+    this._lastProfileHeader = null;
+
+    this._capturingModeSelector = new WebInspector.StatusBarComboBox(this._dispatchViewUpdatedEvent.bind(this));
+    this._capturingModeSelector.element.title = WebInspector.UIString("Canvas capture mode.");
+    this._capturingModeSelector.createOption(WebInspector.UIString("Single Frame"), WebInspector.UIString("Capture a single canvas frame."), "");
+    this._capturingModeSelector.createOption(WebInspector.UIString("Consecutive Frames"), WebInspector.UIString("Capture consecutive canvas frames."), "1");
+
+    /** @type {!Object.<string, Element>} */
+    this._frameOptions = {};
+
+    /** @type {!Object.<string, boolean>} */
+    this._framesWithCanvases = {};
+
+    this._frameSelector = new WebInspector.StatusBarComboBox(this._dispatchViewUpdatedEvent.bind(this));
+    this._frameSelector.element.title = WebInspector.UIString("Frame containing the canvases to capture.");
+    this._frameSelector.element.addStyleClass("hidden");
+    WebInspector.runtimeModel.contextLists().forEach(this._addFrame, this);
+    WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListAdded, this._frameAdded, this);
+    WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListRemoved, this._frameRemoved, this);
+
+    this._decorationElement = document.createElement("div");
+    this._decorationElement.className = "profile-canvas-decoration hidden";
+    this._decorationElement.createChild("div", "warning-icon-small");
+    this._decorationElement.appendChild(document.createTextNode(WebInspector.UIString("There is an uninstrumented canvas on the page. Reload the page to instrument it.")));
+    var reloadPageButton = this._decorationElement.createChild("button");
+    reloadPageButton.type = "button";
+    reloadPageButton.textContent = WebInspector.UIString("Reload");
+    reloadPageButton.addEventListener("click", this._onReloadPageButtonClick.bind(this), false);
+
+    this._dispatcher = new WebInspector.CanvasDispatcher(this);
+
+    // FIXME: enable/disable by a UI action?
+    CanvasAgent.enable(this._updateDecorationElement.bind(this));
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._updateDecorationElement, this);
+}
+
+WebInspector.CanvasProfileType.TypeId = "CANVAS_PROFILE";
+
+WebInspector.CanvasProfileType.prototype = {
+    get statusBarItems()
+    {
+        return [this._capturingModeSelector.element, this._frameSelector.element];
+    },
+
+    get buttonTooltip()
+    {
+        if (this._isSingleFrameMode())
+            return WebInspector.UIString("Capture next canvas frame.");
+        else
+            return this._recording ? WebInspector.UIString("Stop capturing canvas frames.") : WebInspector.UIString("Start capturing canvas frames.");
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    buttonClicked: function()
+    {
+        if (this._recording) {
+            this._recording = false;
+            this._stopFrameCapturing();
+        } else if (this._isSingleFrameMode()) {
+            this._recording = false;
+            this._runSingleFrameCapturing();
+        } else {
+            this._recording = true;
+            this._startFrameCapturing();
+        }
+        return this._recording;
+    },
+
+    _runSingleFrameCapturing: function()
+    {
+        var frameId = this._selectedFrameId();
+        CanvasAgent.captureFrame(frameId, this._didStartCapturingFrame.bind(this, frameId));
+    },
+
+    _startFrameCapturing: function()
+    {
+        var frameId = this._selectedFrameId();
+        CanvasAgent.startCapturing(frameId, this._didStartCapturingFrame.bind(this, frameId));
+    },
+
+    _stopFrameCapturing: function()
+    {
+        if (!this._lastProfileHeader)
+            return;
+        var profileHeader = this._lastProfileHeader;
+        var traceLogId = profileHeader.traceLogId();
+        this._lastProfileHeader = null;
+        function didStopCapturing()
+        {
+            profileHeader._updateCapturingStatus();
+        }
+        CanvasAgent.stopCapturing(traceLogId, didStopCapturing.bind(this));
+    },
+
+    /**
+     * @param {string|undefined} frameId
+     * @param {?Protocol.Error} error
+     * @param {CanvasAgent.TraceLogId} traceLogId
+     */
+    _didStartCapturingFrame: function(frameId, error, traceLogId)
+    {
+        if (error || this._lastProfileHeader && this._lastProfileHeader.traceLogId() === traceLogId)
+            return;
+        var profileHeader = new WebInspector.CanvasProfileHeader(this, WebInspector.UIString("Trace Log %d", this._nextProfileUid), this._nextProfileUid, traceLogId, frameId);
+        ++this._nextProfileUid;
+        this._lastProfileHeader = profileHeader;
+        this.addProfile(profileHeader);
+        profileHeader._updateCapturingStatus();
+    },
+
+    get treeItemTitle()
+    {
+        return WebInspector.UIString("CANVAS PROFILE");
+    },
+
+    get description()
+    {
+        return WebInspector.UIString("Canvas calls instrumentation");
+    },
+
+    /**
+     * @override
+     * @return {Element}
+     */
+    decorationElement: function()
+    {
+        return this._decorationElement;
+    },
+
+    /**
+     * @override
+     */
+    _reset: function()
+    {
+        WebInspector.ProfileType.prototype._reset.call(this);
+        this._nextProfileUid = 1;
+    },
+
+    /**
+     * @override
+     * @param {!WebInspector.ProfileHeader} profile
+     */
+    removeProfile: function(profile)
+    {
+        WebInspector.ProfileType.prototype.removeProfile.call(this, profile);
+        if (this._recording && profile === this._lastProfileHeader)
+            this._recording = false;
+    },
+
+    setRecordingProfile: function(isProfiling)
+    {
+        this._recording = isProfiling;
+    },
+
+    /**
+     * @override
+     * @param {string=} title
+     * @return {!WebInspector.ProfileHeader}
+     */
+    createTemporaryProfile: function(title)
+    {
+        title = title || WebInspector.UIString("Capturing\u2026");
+        return new WebInspector.CanvasProfileHeader(this, title);
+    },
+
+    /**
+     * @override
+     * @param {ProfilerAgent.ProfileHeader} profile
+     * @return {!WebInspector.ProfileHeader}
+     */
+    createProfile: function(profile)
+    {
+        return new WebInspector.CanvasProfileHeader(this, profile.title, -1);
+    },
+
+    _updateDecorationElement: function()
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {boolean} result
+         */
+        function callback(error, result)
+        {
+            var hideWarning = (error || !result);
+            this._decorationElement.enableStyleClass("hidden", hideWarning);
+        }
+        CanvasAgent.hasUninstrumentedCanvases(callback.bind(this));
+    },
+
+    /**
+     * @param {MouseEvent} event
+     */
+    _onReloadPageButtonClick: function(event)
+    {
+        PageAgent.reload(event.shiftKey);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _isSingleFrameMode: function()
+    {
+        return !this._capturingModeSelector.selectedOption().value;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _frameAdded: function(event)
+    {
+        var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data);
+        this._addFrame(contextList);
+    },
+
+    /**
+     * @param {WebInspector.FrameExecutionContextList} contextList
+     */
+    _addFrame: function(contextList)
+    {
+        var frameId = contextList.frameId;
+        var option = document.createElement("option");
+        option.text = contextList.displayName;
+        option.title = contextList.url;
+        option.value = frameId;
+
+        this._frameOptions[frameId] = option;
+
+        if (this._framesWithCanvases[frameId]) {
+            this._frameSelector.addOption(option);
+            this._dispatchViewUpdatedEvent();
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _frameRemoved: function(event)
+    {
+        var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data);
+        var frameId = contextList.frameId;
+        var option = this._frameOptions[frameId];
+        if (option && this._framesWithCanvases[frameId]) {
+            this._frameSelector.removeOption(option);
+            this._dispatchViewUpdatedEvent();
+        }
+        delete this._frameOptions[frameId];
+        delete this._framesWithCanvases[frameId];
+    },
+
+    /**
+     * @param {string} frameId
+     */
+    _contextCreated: function(frameId)
+    {
+        if (this._framesWithCanvases[frameId])
+            return;
+        this._framesWithCanvases[frameId] = true;
+        var option = this._frameOptions[frameId];
+        if (option) {
+            this._frameSelector.addOption(option);
+            this._dispatchViewUpdatedEvent();
+        }
+    },
+
+    /**
+     * @param {NetworkAgent.FrameId=} frameId
+     * @param {CanvasAgent.TraceLogId=} traceLogId
+     */
+    _traceLogsRemoved: function(frameId, traceLogId)
+    {
+        var sidebarElementsToDelete = [];
+        var sidebarElements = /** @type {!Array.<WebInspector.ProfileSidebarTreeElement>} */ ((this.treeElement && this.treeElement.children) || []);
+        for (var i = 0, n = sidebarElements.length; i < n; ++i) {
+            var header = /** @type {WebInspector.CanvasProfileHeader} */ (sidebarElements[i].profile);
+            if (!header)
+                continue;
+            if (frameId && frameId !== header.frameId())
+                continue;
+            if (traceLogId && traceLogId !== header.traceLogId())
+                continue;
+            sidebarElementsToDelete.push(sidebarElements[i]);
+        }
+        for (var i = 0, n = sidebarElementsToDelete.length; i < n; ++i)
+            sidebarElementsToDelete[i].ondelete();
+    },
+
+    /**
+     * @return {string|undefined}
+     */
+    _selectedFrameId: function()
+    {
+        var option = this._frameSelector.selectedOption();
+        return option ? option.value : undefined;
+    },
+
+    _dispatchViewUpdatedEvent: function()
+    {
+        this._frameSelector.element.enableStyleClass("hidden", this._frameSelector.size() <= 1);
+        this.dispatchEventToListeners(WebInspector.ProfileType.Events.ViewUpdated);
+    },
+
+    __proto__: WebInspector.ProfileType.prototype
+}
+
+/**
+ * @constructor
+ * @implements {CanvasAgent.Dispatcher}
+ * @param {WebInspector.CanvasProfileType} profileType
+ */
+WebInspector.CanvasDispatcher = function(profileType)
+{
+    this._profileType = profileType;
+    InspectorBackend.registerCanvasDispatcher(this);
+}
+
+WebInspector.CanvasDispatcher.prototype = {
+    /**
+     * @param {string} frameId
+     */
+    contextCreated: function(frameId)
+    {
+        this._profileType._contextCreated(frameId);
+    },
+
+    /**
+     * @param {NetworkAgent.FrameId=} frameId
+     * @param {CanvasAgent.TraceLogId=} traceLogId
+     */
+    traceLogsRemoved: function(frameId, traceLogId)
+    {
+        this._profileType._traceLogsRemoved(frameId, traceLogId);
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileHeader}
+ * @param {!WebInspector.CanvasProfileType} type
+ * @param {string} title
+ * @param {number=} uid
+ * @param {CanvasAgent.TraceLogId=} traceLogId
+ * @param {NetworkAgent.FrameId=} frameId
+ */
+WebInspector.CanvasProfileHeader = function(type, title, uid, traceLogId, frameId)
+{
+    WebInspector.ProfileHeader.call(this, type, title, uid);
+    /** @type {CanvasAgent.TraceLogId} */
+    this._traceLogId = traceLogId || "";
+    this._frameId = frameId;
+    this._alive = true;
+    this._traceLogSize = 0;
+}
+
+WebInspector.CanvasProfileHeader.prototype = {
+    /**
+     * @return {CanvasAgent.TraceLogId}
+     */
+    traceLogId: function()
+    {
+        return this._traceLogId;
+    },
+
+    /**
+     * @return {NetworkAgent.FrameId|undefined}
+     */
+    frameId: function()
+    {
+        return this._frameId;
+    },
+
+    /**
+     * @override
+     * @return {WebInspector.ProfileSidebarTreeElement}
+     */
+    createSidebarTreeElement: function()
+    {
+        return new WebInspector.ProfileSidebarTreeElement(this, WebInspector.UIString("Trace Log %d"), "profile-sidebar-tree-item");
+    },
+
+    /**
+     * @override
+     * @param {WebInspector.ProfilesPanel} profilesPanel
+     */
+    createView: function(profilesPanel)
+    {
+        return new WebInspector.CanvasProfileView(this);
+    },
+
+    /**
+     * @override
+     */
+    dispose: function()
+    {
+        if (this._traceLogId) {
+            CanvasAgent.dropTraceLog(this._traceLogId);
+            clearTimeout(this._requestStatusTimer);
+            this._alive = false;
+        }
+    },
+
+    /**
+     * @param {CanvasAgent.TraceLog=} traceLog
+     */
+    _updateCapturingStatus: function(traceLog)
+    {
+        if (!this.sidebarElement || !this._traceLogId)
+            return;
+
+        if (traceLog) {
+            this._alive = traceLog.alive;
+            this._traceLogSize = traceLog.totalAvailableCalls;
+        }
+
+        this.sidebarElement.subtitle = this._alive ? WebInspector.UIString("Capturing\u2026 %d calls", this._traceLogSize) : WebInspector.UIString("Captured %d calls", this._traceLogSize);
+        this.sidebarElement.wait = this._alive;
+
+        if (this._alive) {
+            clearTimeout(this._requestStatusTimer);
+            this._requestStatusTimer = setTimeout(this._requestCapturingStatus.bind(this), WebInspector.CanvasProfileView.TraceLogPollingInterval);
+        }
+    },
+
+    _requestCapturingStatus: function()
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {CanvasAgent.TraceLog} traceLog
+         */
+        function didReceiveTraceLog(error, traceLog)
+        {
+            if (error)
+                return;
+            this._alive = traceLog.alive;
+            this._traceLogSize = traceLog.totalAvailableCalls;
+            this._updateCapturingStatus();
+        }
+        CanvasAgent.getTraceLog(this._traceLogId, 0, 0, didReceiveTraceLog.bind(this));
+    },
+
+    __proto__: WebInspector.ProfileHeader.prototype
+}
diff --git a/Source/devtools/front_end/Checkbox.js b/Source/devtools/front_end/Checkbox.js
new file mode 100644
index 0000000..f71590f
--- /dev/null
+++ b/Source/devtools/front_end/Checkbox.js
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @param {string=} tooltip
+ */
+WebInspector.Checkbox = function(label, className, tooltip)
+{
+    this.element = document.createElement('label');
+    this._inputElement = document.createElement('input');
+    this._inputElement.type = "checkbox";
+
+    this.element.className = className;
+    this.element.appendChild(this._inputElement);
+    this.element.appendChild(document.createTextNode(label));
+    if (tooltip)
+        this.element.title = tooltip;
+}
+
+WebInspector.Checkbox.prototype = {
+    set checked(checked)
+    {
+        this._inputElement.checked = checked;
+    },
+
+    get checked()
+    {
+        return this._inputElement.checked;
+    },
+
+    addEventListener: function(listener)
+    {
+        function listenerWrapper(event)
+        {
+            if (listener)
+                listener(event);
+            event.consume();
+            return true;
+        }
+
+        this._inputElement.addEventListener("click", listenerWrapper, false);
+        this.element.addEventListener("click", listenerWrapper, false);
+    }
+}
diff --git a/Source/devtools/front_end/CodeMirrorTextEditor.js b/Source/devtools/front_end/CodeMirrorTextEditor.js
new file mode 100644
index 0000000..306e7e5
--- /dev/null
+++ b/Source/devtools/front_end/CodeMirrorTextEditor.js
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+importScript("cm/codemirror.js");
+importScript("cm/css.js");
+importScript("cm/javascript.js");
+importScript("cm/xml.js");
+importScript("cm/htmlmixed.js");
+importScript("cm/matchbrackets.js");
+importScript("cm/closebrackets.js");
+importScript("cm/markselection.js");
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @implements {WebInspector.TextEditor}
+ * @param {?string} url
+ * @param {WebInspector.TextEditorDelegate} delegate
+ */
+WebInspector.CodeMirrorTextEditor = function(url, delegate)
+{
+    WebInspector.View.call(this);
+    this._delegate = delegate;
+    this._url = url;
+
+    this.registerRequiredCSS("cm/codemirror.css");
+    this.registerRequiredCSS("cm/cmdevtools.css");
+
+    this._codeMirror = window.CodeMirror(this.element, {
+        lineNumbers: true,
+        gutters: ["CodeMirror-linenumbers"],
+        matchBrackets: true,
+        smartIndent: false,
+        styleSelectedText: true,
+        electricChars: false,
+        autoCloseBrackets: true
+    });
+
+    var extraKeys = {};
+    var indent = WebInspector.settings.textEditorIndent.get();
+    if (indent === WebInspector.TextUtils.Indent.TabCharacter) {
+        this._codeMirror.setOption("indentWithTabs", true);
+        this._codeMirror.setOption("indentUnit", 4);
+    } else {
+        this._codeMirror.setOption("indentWithTabs", false);
+        this._codeMirror.setOption("indentUnit", indent.length);
+        extraKeys.Tab = function(codeMirror)
+        {
+            if (codeMirror.somethingSelected())
+                return CodeMirror.Pass;
+            codeMirror.replaceRange(indent, codeMirror.getCursor());
+        }
+    }
+    this._codeMirror.setOption("extraKeys", extraKeys);
+
+    this._tokenHighlighter = new WebInspector.CodeMirrorTextEditor.TokenHighlighter(this._codeMirror);
+    this._blockIndentController = new WebInspector.CodeMirrorTextEditor.BlockIndentController(this._codeMirror);
+    this._fixWordMovement = new WebInspector.CodeMirrorTextEditor.FixWordMovement(this._codeMirror);
+
+    this._codeMirror.on("change", this._change.bind(this));
+    this._codeMirror.on("gutterClick", this._gutterClick.bind(this));
+    this._codeMirror.on("cursorActivity", this._cursorActivity.bind(this));
+    this._codeMirror.on("scroll", this._scroll.bind(this));
+    this.element.addEventListener("contextmenu", this._contextMenu.bind(this));
+
+    this._lastRange = this.range();
+
+    this.element.firstChild.addStyleClass("source-code");
+    this.element.firstChild.addStyleClass("fill");
+    this._elementToWidget = new Map();
+    this._nestedUpdatesCounter = 0;
+
+    this.element.addEventListener("focus", this._handleElementFocus.bind(this), false);
+    this.element.tabIndex = 0;
+    this._setupSelectionColor();
+}
+
+WebInspector.CodeMirrorTextEditor.prototype = {
+
+    undo: function()
+    {
+        this._codeMirror.undo();
+    },
+
+    redo: function()
+    {
+        this._codeMirror.redo();
+    },
+
+    _setupSelectionColor: function()
+    {
+        if (WebInspector.CodeMirrorTextEditor._selectionStyleInjected)
+            return;
+        WebInspector.CodeMirrorTextEditor._selectionStyleInjected = true;
+        var backgroundColor = WebInspector.getSelectionBackgroundColor();
+        var backgroundColorRule = backgroundColor ? ".CodeMirror .CodeMirror-selected { background-color: " + backgroundColor + ";}" : "";
+        var foregroundColor = WebInspector.getSelectionForegroundColor();
+        var foregroundColorRule = foregroundColor ? ".CodeMirror .CodeMirror-selectedtext { color: " + foregroundColor + "!important;}" : "";
+        if (!foregroundColorRule && !backgroundColorRule)
+            return;
+
+        var style = document.createElement("style");
+        style.textContent = backgroundColorRule + foregroundColorRule;
+        document.head.appendChild(style);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @return {?{x: number, y: number, height: number}}
+     */
+    cursorPositionToCoordinates: function(lineNumber, column)
+    {
+        if (lineNumber >= this._codeMirror.lineCount || column > this._codeMirror.getLine(lineNumber).length || lineNumber < 0 || column < 0)
+            return null;
+
+        var metrics = this._codeMirror.cursorCoords(new CodeMirror.Pos(lineNumber, column));
+
+        return {
+            x: metrics.left,
+            y: metrics.top,
+            height: metrics.bottom - metrics.top
+        };
+    },
+
+    /**
+     * @param {number} x
+     * @param {number} y
+     * @return {?WebInspector.TextRange}
+     */
+    coordinatesToCursorPosition: function(x, y)
+    {
+        var element = document.elementFromPoint(x, y);
+        if (!element || !element.isSelfOrDescendant(this._codeMirror.getWrapperElement()))
+            return null;
+        var gutterBox = this._codeMirror.getGutterElement().boxInWindow();
+        if (x >= gutterBox.x && x <= gutterBox.x + gutterBox.width &&
+            y >= gutterBox.y && y <= gutterBox.y + gutterBox.height)
+            return null;
+        var coords = this._codeMirror.coordsChar({left: x, top: y});
+        ++coords.ch;
+        return this._toRange(coords, coords);
+    },
+
+    _convertTokenType: function(tokenType)
+    {
+        if (tokenType.startsWith("variable") || tokenType.startsWith("property") || tokenType === "def")
+            return "javascript-ident";
+        if (tokenType === "string-2")
+            return "javascript-regexp";
+        if (tokenType === "number" || tokenType === "comment" || tokenType === "string")
+            return "javascript-" + tokenType;
+        return null;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @return {?{startColumn: number, endColumn: number, type: string}}
+     */
+    tokenAtTextPosition: function(lineNumber, column)
+    {
+        if (lineNumber < 0 || lineNumber >= this._codeMirror.lineCount())
+            return null;
+        var token = this._codeMirror.getTokenAt(new CodeMirror.Pos(lineNumber, column || 1));
+        if (!token || !token.type)
+            return null;
+        var convertedType = this._convertTokenType(token.type);
+        if (!convertedType)
+            return null;
+        return {
+            startColumn: token.start,
+            endColumn: token.end - 1,
+            type: convertedType
+        };
+    },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     * @return {string}
+     */
+    copyRange: function(textRange)
+    {
+        var pos = this._toPos(textRange);
+        return this._codeMirror.getRange(pos.start, pos.end);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isClean: function()
+    {
+        return this._codeMirror.isClean();
+    },
+
+    markClean: function()
+    {
+        this._codeMirror.markClean();
+    },
+
+    /**
+     * @param {string} mimeType
+     */
+    set mimeType(mimeType)
+    {
+        this._codeMirror.setOption("mode", mimeType);
+        switch(mimeType) {
+            case "text/html": this._codeMirror.setOption("theme", "web-inspector-html"); break;
+            case "text/css": this._codeMirror.setOption("theme", "web-inspector-css"); break;
+            case "text/javascript": this._codeMirror.setOption("theme", "web-inspector-js"); break;
+        }
+    },
+
+    /**
+     * @param {boolean} readOnly
+     */
+    setReadOnly: function(readOnly)
+    {
+        this._codeMirror.setOption("readOnly", readOnly ? "nocursor" : false);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    readOnly: function()
+    {
+        return !!this._codeMirror.getOption("readOnly");
+    },
+
+    /**
+     * @param {Object} highlightDescriptor
+     */
+    removeHighlight: function(highlightDescriptor)
+    {
+        highlightDescriptor.clear();
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} cssClass
+     * @return {Object}
+     */
+    highlightRange: function(range, cssClass)
+    {
+        var pos = this._toPos(range);
+        ++pos.end.ch;
+        return this._codeMirror.markText(pos.start, pos.end, {
+            className: cssClass,
+            startStyle: cssClass + "-start",
+            endStyle: cssClass + "-end"
+        });
+    },
+
+    /**
+     * @param {string} regex
+     * @param {string} cssClass
+     * @return {Object}
+     */
+    highlightRegex: function(regex, cssClass) { },
+
+    /**
+     * @return {Element}
+     */
+    defaultFocusedElement: function()
+    {
+        return this.element;
+    },
+
+    focus: function()
+    {
+        this._codeMirror.focus();
+    },
+
+    _handleElementFocus: function()
+    {
+        this._codeMirror.focus();
+    },
+
+    beginUpdates: function()
+    {
+        ++this._nestedUpdatesCounter;
+    },
+
+    endUpdates: function()
+    {
+        if (!--this._nestedUpdatesCounter)
+            this._codeMirror.refresh();
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    revealLine: function(lineNumber)
+    {
+        var pos = new CodeMirror.Pos(lineNumber, 0);
+        var topLine = this._topScrolledLine();
+        var bottomLine = this._bottomScrolledLine();
+
+        var margin = null;
+        var lineMargin = 3;
+        var scrollInfo = this._codeMirror.getScrollInfo();
+        if ((lineNumber < topLine + lineMargin) || (lineNumber >= bottomLine - lineMargin)) {
+            // scrollIntoView could get into infinite loop if margin exceeds half of the clientHeight.
+            margin = (scrollInfo.clientHeight*0.9/2) >>> 0;
+        }
+        this._codeMirror.scrollIntoView(pos, margin);
+    },
+
+    _gutterClick: function(instance, lineNumber, gutter, event)
+    {
+        this.dispatchEventToListeners(WebInspector.TextEditor.Events.GutterClick, { lineNumber: lineNumber, event: event });
+    },
+
+    _contextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        var target = event.target.enclosingNodeOrSelfWithClass("CodeMirror-gutter-elt");
+        if (target)
+            this._delegate.populateLineGutterContextMenu(contextMenu, parseInt(target.textContent, 10) - 1);
+        else
+            this._delegate.populateTextAreaContextMenu(contextMenu, 0);
+        contextMenu.show();
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {boolean} disabled
+     * @param {boolean} conditional
+     */
+    addBreakpoint: function(lineNumber, disabled, conditional)
+    {
+        var className = "cm-breakpoint" + (conditional ? " cm-breakpoint-conditional" : "") + (disabled ? " cm-breakpoint-disabled" : "");
+        this._codeMirror.addLineClass(lineNumber, "wrap", className);
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    removeBreakpoint: function(lineNumber)
+    {
+        var wrapClasses = this._codeMirror.getLineHandle(lineNumber).wrapClass;
+        if (!wrapClasses)
+            return;
+        var classes = wrapClasses.split(" ");
+        for(var i = 0; i < classes.length; ++i) {
+            if (classes[i].startsWith("cm-breakpoint"))
+                this._codeMirror.removeLineClass(lineNumber, "wrap", classes[i]);
+        }
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    setExecutionLine: function(lineNumber)
+    {
+        this._executionLine = this._codeMirror.getLineHandle(lineNumber);
+        this._codeMirror.addLineClass(this._executionLine, "wrap", "cm-execution-line");
+    },
+
+    clearExecutionLine: function()
+    {
+        if (this._executionLine)
+            this._codeMirror.removeLineClass(this._executionLine, "wrap", "cm-execution-line");
+        delete this._executionLine;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {Element} element
+     */
+    addDecoration: function(lineNumber, element)
+    {
+        var widget = this._codeMirror.addLineWidget(lineNumber, element);
+        this._elementToWidget.put(element, widget);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {Element} element
+     */
+    removeDecoration: function(lineNumber, element)
+    {
+        var widget = this._elementToWidget.remove(element);
+        if (widget)
+            this._codeMirror.removeLineWidget(widget);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     */
+    markAndRevealRange: function(range)
+    {
+        if (range)
+            this.setSelection(range);
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    highlightLine: function(lineNumber)
+    {
+        this.clearLineHighlight();
+        this._highlightedLine = this._codeMirror.getLineHandle(lineNumber);
+        if (!this._highlightedLine)
+          return;
+        this.revealLine(lineNumber);
+        this._codeMirror.addLineClass(this._highlightedLine, null, "cm-highlight");
+        this._clearHighlightTimeout = setTimeout(this.clearLineHighlight.bind(this), 2000);
+    },
+
+    clearLineHighlight: function()
+    {
+        if (this._clearHighlightTimeout)
+            clearTimeout(this._clearHighlightTimeout);
+        delete this._clearHighlightTimeout;
+
+         if (this._highlightedLine)
+            this._codeMirror.removeLineClass(this._highlightedLine, null, "cm-highlight");
+        delete this._highlightedLine;
+    },
+
+    /**
+     * @return {Array.<Element>}
+     */
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return [];
+    },
+
+    /**
+     * @param {WebInspector.TextEditor} textEditor
+     */
+    inheritScrollPositions: function(textEditor)
+    {
+    },
+
+    onResize: function()
+    {
+        this._codeMirror.refresh();
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} text
+     * @return {WebInspector.TextRange}
+     */
+    editRange: function(range, text)
+    {
+        var pos = this._toPos(range);
+        this._codeMirror.replaceRange(text, pos.start, pos.end);
+        var newRange = this._toRange(pos.start, this._codeMirror.posFromIndex(this._codeMirror.indexFromPos(pos.start) + text.length));
+        this._delegate.onTextChanged(range, newRange);
+        return newRange;
+    },
+
+    _change: function()
+    {
+        var widgets = this._elementToWidget.values();
+        for (var i = 0; i < widgets.length; ++i)
+            this._codeMirror.removeLineWidget(widgets[i]);
+        this._elementToWidget.clear();
+
+        var newRange = this.range();
+        if (!this._muteTextChangedEvent)
+            this._delegate.onTextChanged(this._lastRange, newRange);
+        this._lastRange = newRange;
+    },
+
+    _cursorActivity: function()
+    {
+        var start = this._codeMirror.getCursor("anchor");
+        var end = this._codeMirror.getCursor("head");
+        this._delegate.selectionChanged(this._toRange(start, end));
+    },
+
+    _coordsCharLocal: function(coords)
+    {
+        var top = coords.top;
+        var totalLines = this._codeMirror.lineCount();
+        var begin = 0;
+        var end = totalLines - 1;
+        while (end - begin > 1) {
+            var middle = (begin + end) >> 1;
+            coords = this._codeMirror.charCoords(new CodeMirror.Pos(middle, 0), "local");
+            if (coords.top >= top)
+                end = middle;
+            else
+                begin = middle;
+        }
+
+        return end;
+    },
+
+    _topScrolledLine: function()
+    {
+        var scrollInfo = this._codeMirror.getScrollInfo();
+        // Workaround for CodeMirror's coordsChar incorrect result for "local" mode.
+        return this._coordsCharLocal(scrollInfo);
+    },
+
+    _bottomScrolledLine: function()
+    {
+        var scrollInfo = this._codeMirror.getScrollInfo();
+        scrollInfo.top += scrollInfo.clientHeight;
+        // Workaround for CodeMirror's coordsChar incorrect result for "local" mode.
+        return this._coordsCharLocal(scrollInfo);
+    },
+
+    _scroll: function()
+    {
+        this._delegate.scrollChanged(this._topScrolledLine());
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollToLine: function(lineNumber)
+    {
+        function performScroll()
+        {
+            var pos = new CodeMirror.Pos(lineNumber, 0);
+            var coords = this._codeMirror.charCoords(pos, "local");
+            this._codeMirror.scrollTo(0, coords.top);
+        }
+
+        setTimeout(performScroll.bind(this), 0);
+    },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    selection: function()
+    {
+        var start = this._codeMirror.getCursor(true);
+        var end = this._codeMirror.getCursor(false);
+
+        if (start.line > end.line || (start.line == end.line && start.ch > end.ch))
+            return this._toRange(end, start);
+
+        return this._toRange(start, end);
+    },
+
+    /**
+     * @return {WebInspector.TextRange?}
+     */
+    lastSelection: function()
+    {
+        return this._lastSelection;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    setSelection: function(textRange)
+    {
+        function performSelectionSet()
+        {
+            this._lastSelection = textRange;
+            var pos = this._toPos(textRange);
+            this._codeMirror.setSelection(pos.start, pos.end);
+        }
+
+        setTimeout(performSelectionSet.bind(this), 0);
+    },
+
+    /**
+     * @param {string} text
+     */
+    setText: function(text)
+    {
+        this._muteTextChangedEvent = true;
+        this._codeMirror.setValue(text);
+        this._codeMirror.clearHistory();
+        delete this._muteTextChangedEvent;
+    },
+
+    /**
+     * @return {string}
+     */
+    text: function()
+    {
+        return this._codeMirror.getValue();
+    },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    range: function()
+    {
+        var lineCount = this.linesCount;
+        var lastLine = this._codeMirror.getLine(lineCount - 1);
+        return this._toRange(new CodeMirror.Pos(0, 0), new CodeMirror.Pos(lineCount - 1, lastLine.length));
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {string}
+     */
+    line: function(lineNumber)
+    {
+        return this._codeMirror.getLine(lineNumber);
+    },
+
+    /**
+     * @return {number}
+     */
+    get linesCount()
+    {
+        return this._codeMirror.lineCount();
+    },
+
+    /**
+     * @param {number} line
+     * @param {string} name
+     * @param {Object?} value
+     */
+    setAttribute: function(line, name, value)
+    {
+        var handle = this._codeMirror.getLineHandle(line);
+        if (handle.attributes === undefined) handle.attributes = {};
+        handle.attributes[name] = value;
+    },
+
+    /**
+     * @param {number} line
+     * @param {string} name
+     * @return {Object|null} value
+     */
+    getAttribute: function(line, name)
+    {
+        var handle = this._codeMirror.getLineHandle(line);
+        return handle.attributes && handle.attributes[name] !== undefined ? handle.attributes[name] : null;
+    },
+
+    /**
+     * @param {number} line
+     * @param {string} name
+     */
+    removeAttribute: function(line, name)
+    {
+        var handle = this._codeMirror.getLineHandle(line);
+        if (handle && handle.attributes)
+            delete handle.attributes[name];
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {{start: CodeMirror.Pos, end: CodeMirror.Pos}}
+     */
+    _toPos: function(range)
+    {
+        return {
+            start: new CodeMirror.Pos(range.startLine, range.startColumn),
+            end: new CodeMirror.Pos(range.endLine, range.endColumn)
+        }
+    },
+
+    _toRange: function(start, end)
+    {
+        return new WebInspector.TextRange(start.line, start.ch, end.line, end.ch);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @param {CodeMirror} codeMirror
+ */
+WebInspector.CodeMirrorTextEditor.TokenHighlighter = function(codeMirror)
+{
+    this._codeMirror = codeMirror;
+    this._codeMirror.on("cursorActivity", this._cursorChange.bind(this));
+}
+
+WebInspector.CodeMirrorTextEditor.TokenHighlighter.prototype = {
+    _cursorChange: function()
+    {
+        this._codeMirror.operation(this._removeHighlight.bind(this));
+        var selectionStart = this._codeMirror.getCursor("start");
+        var selectionEnd = this._codeMirror.getCursor("end");
+        if (selectionStart.line !== selectionEnd.line)
+            return;
+        if (selectionStart.ch === selectionEnd.ch)
+            return;
+
+        var selectedText = this._codeMirror.getSelection();
+        if (this._isWord(selectedText, selectionStart.line, selectionStart.ch, selectionEnd.ch))
+            this._codeMirror.operation(this._addHighlight.bind(this, selectedText, selectionStart));
+    },
+
+    _isWord: function(selectedText, lineNumber, startColumn, endColumn)
+    {
+        var line = this._codeMirror.getLine(lineNumber);
+        var leftBound = startColumn === 0 || !WebInspector.TextUtils.isWordChar(line.charAt(startColumn - 1));
+        var rightBound = endColumn === line.length || !WebInspector.TextUtils.isWordChar(line.charAt(endColumn));
+        return leftBound && rightBound && WebInspector.TextUtils.isWord(selectedText);
+    },
+
+    _removeHighlight: function()
+    {
+        if (this._highlightDescriptor) {
+            this._codeMirror.removeOverlay(this._highlightDescriptor.overlay);
+            this._codeMirror.removeLineClass(this._highlightDescriptor.selectionStart.line, "wrap", "cm-line-with-selection");
+            delete this._highlightDescriptor;
+        }
+    },
+
+    _addHighlight: function(token, selectionStart)
+    {
+        const tokenFirstChar = token.charAt(0);
+        /**
+         * @param {CodeMirror.StringStream} stream
+         */
+        function nextToken(stream)
+        {
+            if (stream.match(token) && (stream.eol() || !WebInspector.TextUtils.isWordChar(stream.peek())))
+                return stream.column() === selectionStart.ch ? "token-highlight column-with-selection" : "token-highlight";
+
+            var eatenChar;
+            do {
+                eatenChar = stream.next();
+            } while (eatenChar && (WebInspector.TextUtils.isWordChar(eatenChar) || stream.peek() !== tokenFirstChar));
+        }
+
+        var overlayMode = {
+            token: nextToken
+        };
+        this._codeMirror.addOverlay(overlayMode);
+        this._codeMirror.addLineClass(selectionStart.line, "wrap", "cm-line-with-selection")
+        this._highlightDescriptor = {
+            overlay: overlayMode,
+            selectionStart: selectionStart
+        };
+    }
+}
+
+/**
+ * @constructor
+ * @param {CodeMirror} codeMirror
+ */
+WebInspector.CodeMirrorTextEditor.BlockIndentController = function(codeMirror)
+{
+    codeMirror.addKeyMap(this);
+}
+
+WebInspector.CodeMirrorTextEditor.BlockIndentController.prototype = {
+    name: "blockIndentKeymap",
+
+    Enter: function(codeMirror)
+    {
+        if (codeMirror.somethingSelected())
+            return CodeMirror.Pass;
+        var cursor = codeMirror.getCursor();
+        var line = codeMirror.getLine(cursor.line);
+        if (line.substr(cursor.ch - 1, 2) === "{}") {
+            codeMirror.execCommand("newlineAndIndent");
+            codeMirror.setCursor(cursor);
+            codeMirror.execCommand("newlineAndIndent");
+            codeMirror.execCommand("indentMore");
+        } else if (line.substr(cursor.ch-1, 1) === "{") {
+            codeMirror.execCommand("newlineAndIndent");
+            codeMirror.execCommand("indentMore");
+        } else
+            return CodeMirror.Pass;
+    },
+
+    "'}'": function(codeMirror)
+    {
+        var cursor = codeMirror.getCursor();
+        var line = codeMirror.getLine(cursor.line);
+        for(var i = 0 ; i < line.length; ++i)
+            if (!WebInspector.TextUtils.isSpaceChar(line.charAt(i)))
+                return CodeMirror.Pass;
+
+        codeMirror.replaceRange("}", cursor);
+        var matchingBracket = codeMirror.findMatchingBracket();
+        if (!matchingBracket.match)
+            return;
+
+        line = codeMirror.getLine(matchingBracket.to.line);
+        var desiredIndentation = 0;
+        while (desiredIndentation < line.length && WebInspector.TextUtils.isSpaceChar(line.charAt(desiredIndentation)))
+            ++desiredIndentation;
+
+        codeMirror.replaceRange(line.substr(0, desiredIndentation) + "}", new CodeMirror.Pos(cursor.line, 0), new CodeMirror.Pos(cursor.line, cursor.ch + 1));
+    }
+}
+
+/**
+ * @constructor
+ * @param {CodeMirror} codeMirror
+ */
+WebInspector.CodeMirrorTextEditor.FixWordMovement = function(codeMirror)
+{
+    function moveLeft(shift, codeMirror)
+    {
+        var cursor = codeMirror.getCursor("head");
+        if (cursor.ch !== 0 || cursor.line === 0)
+            return CodeMirror.Pass;
+        codeMirror.setExtending(shift);
+        codeMirror.execCommand("goLineUp");
+        codeMirror.execCommand("goLineEnd")
+        codeMirror.setExtending(false);
+    }
+    function moveRight(shift, codeMirror)
+    {
+        var cursor = codeMirror.getCursor("head");
+        var line = codeMirror.getLine(cursor.line);
+        if (cursor.ch !== line.length || cursor.line + 1 === codeMirror.lineCount())
+            return CodeMirror.Pass;
+        codeMirror.setExtending(shift);
+        codeMirror.execCommand("goLineDown");
+        codeMirror.execCommand("goLineStart");
+        codeMirror.setExtending(false);
+    }
+
+    var modifierKey = WebInspector.isMac() ? "Alt" : "Ctrl";
+    var leftKey = modifierKey + "-Left";
+    var rightKey = modifierKey + "-Right";
+    var keyMap = {};
+    keyMap[leftKey] = moveLeft.bind(this, false);
+    keyMap[rightKey] = moveRight.bind(this, false);
+    keyMap["Shift-" + leftKey] = moveLeft.bind(this, true);
+    keyMap["Shift-" + rightKey] = moveRight.bind(this, true);
+    codeMirror.addKeyMap(keyMap);
+}
diff --git a/Source/devtools/front_end/Color.js b/Source/devtools/front_end/Color.js
new file mode 100644
index 0000000..701ee98
--- /dev/null
+++ b/Source/devtools/front_end/Color.js
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2009 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 Computer, 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.
+ */
+
+/**
+ * @param {Array.<number>} rgba
+ * @param {string=} format
+ * @param {string=} originalText
+ * @constructor
+ */
+WebInspector.Color = function(rgba, format, originalText)
+{
+    this._rgba = rgba;
+    this._originalText = originalText || null;
+    this._format = format || null;
+    if (typeof this._rgba[3] === "undefined")
+        this._rgba[3] = 1;
+    for (var i = 0; i < 4; ++i) {
+        if (this._rgba[i] < 0)
+            this._rgba[i] = 0;
+        if (this._rgba[i] > 1)
+            this._rgba[i] = 1;
+    }
+}
+
+/**
+ * @param {string} text
+ * @return {?WebInspector.Color}
+ */
+WebInspector.Color.parse = function(text)
+{
+    // Simple - #hex, rgb(), nickname, hsl()
+    var value = text.toLowerCase().replace(/\s+/g, "");
+    var simple = /^(?:#([0-9a-f]{3,6})|rgb\(([^)]+)\)|(\w+)|hsl\(([^)]+)\))$/i;
+    var match = value.match(simple);
+    if (match) {
+        if (match[1]) { // hex
+            var hex = match[1].toUpperCase();
+            var format;
+            if (hex.length === 3) {
+                format = WebInspector.Color.Format.ShortHEX;
+                hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
+            } else
+                format = WebInspector.Color.Format.HEX;
+            var r = parseInt(hex.substring(0,2), 16);
+            var g = parseInt(hex.substring(2,4), 16);
+            var b = parseInt(hex.substring(4,6), 16);
+            return new WebInspector.Color([r / 255, g / 255, b / 255, 1], format, text);
+        }
+
+        if (match[2]) { // rgb
+            var rgbString = match[2].split(/\s*,\s*/);
+            var rgba = [ WebInspector.Color._parseRgbNumeric(rgbString[0]),
+                         WebInspector.Color._parseRgbNumeric(rgbString[1]),
+                         WebInspector.Color._parseRgbNumeric(rgbString[2]), 1 ];
+            return new WebInspector.Color(rgba, WebInspector.Color.Format.RGB, text);
+        }
+
+        if (match[3]) { // nickname
+            var nickname = match[3].toLowerCase();
+            if (nickname in WebInspector.Color.Nicknames) {
+                var rgba = WebInspector.Color.Nicknames[nickname];
+                var color = WebInspector.Color.fromRGBA(rgba);
+                color._format = WebInspector.Color.Format.Nickname;
+                color._originalText = nickname;
+                return color;
+            }
+            return null;
+        }
+
+        if (match[4]) { // hsl
+            var hslString = match[4].replace(/%/g, "").split(/\s*,\s*/);
+            var hsla = [ WebInspector.Color._parseHueNumeric(hslString[0]),
+                         WebInspector.Color._parseSatLightNumeric(hslString[1]),
+                         WebInspector.Color._parseSatLightNumeric(hslString[2]), 1 ];
+            var rgba = WebInspector.Color._hsl2rgb(hsla);
+            return new WebInspector.Color(rgba, WebInspector.Color.Format.HSL, text);
+        }
+
+        return null;
+    }
+
+    // Advanced - rgba(), hsla()
+    var advanced = /^(?:rgba\(([^)]+)\)|hsla\(([^)]+)\))$/;
+    match = value.match(advanced);
+    if (match) {
+        if (match[1]) { // rgba
+            var rgbaString = match[1].split(/\s*,\s*/);
+            var rgba = [ WebInspector.Color._parseRgbNumeric(rgbaString[0]),
+                         WebInspector.Color._parseRgbNumeric(rgbaString[1]),
+                         WebInspector.Color._parseRgbNumeric(rgbaString[2]),
+                         WebInspector.Color._parseAlphaNumeric(rgbaString[3]) ];
+            return new WebInspector.Color(rgba, WebInspector.Color.Format.RGBA, text);
+        }
+
+        if (match[2]) { // hsla
+            var hslaString = match[2].replace(/%/g, "").split(/\s*,\s*/);
+            var hsla = [ WebInspector.Color._parseHueNumeric(hslaString[0]),
+                         WebInspector.Color._parseSatLightNumeric(hslaString[1]),
+                         WebInspector.Color._parseSatLightNumeric(hslaString[2]),
+                         WebInspector.Color._parseAlphaNumeric(hslaString[3]) ];
+            var rgba = WebInspector.Color._hsl2rgb(hsla);
+            return new WebInspector.Color(rgba, WebInspector.Color.Format.HSLA, text);
+        }
+    }
+
+    return null;
+}
+
+/**
+ * @param {Array.<number>} rgba
+ * @return {WebInspector.Color}
+ */
+WebInspector.Color.fromRGBA = function(rgba)
+{
+    return new WebInspector.Color([rgba[0] / 255, rgba[1] / 255, rgba[2] / 255, rgba[3]]);
+}
+
+/**
+ * @param {Array.<number>} hsva
+ * @return {WebInspector.Color}
+ */
+WebInspector.Color.fromHSVA = function(hsva)
+{
+    var h = hsva[0];
+    var s = hsva[1];
+    var v = hsva[2];
+
+    var t = (2 - s) * v;
+    if (v === 0 || s === 0)
+        s = 0;
+    else
+        s *= v / (t < 1 ? t : 2 - t);
+    var hsla = [h, s, t / 2, hsva[3]];
+
+    return new WebInspector.Color(WebInspector.Color._hsl2rgb(hsla), WebInspector.Color.Format.HSLA);
+}
+
+WebInspector.Color.prototype = {
+    /**
+     * @return {?string}
+     */
+    format: function()
+    {
+        return this._format;
+    },
+
+    /**
+     * @return {Array.<number>} HSLA with components within [0..1]
+     */
+    hsla: function()
+    {
+        if (this._hsla)
+            return this._hsla;
+        var r = this._rgba[0];
+        var g = this._rgba[1];
+        var b = this._rgba[2];
+        var max = Math.max(r, g, b);
+        var min = Math.min(r, g, b);
+        var diff = max - min;
+        var add = max + min;
+
+        if (min === max)
+            var h = 0;
+        else if (r === max)
+            var h = ((1/6 * (g - b) / diff) + 1) % 1;
+        else if (g === max)
+            var h = (1/6 * (b - r) / diff) + 1/3;
+        else
+            var h = (1/6 * (r - g) / diff) + 2/3;
+
+        var l = 0.5 * add;
+
+        if (l === 0)
+            var s = 0;
+        else if (l === 1)
+            var s = 1;
+        else if (l <= 0.5)
+            var s = diff / add;
+        else
+            var s = diff / (2 - add);
+
+        this._hsla = [h, s, l, this._rgba[3]];
+        return this._hsla;
+    },
+
+    /**
+     * @return {Array.<number>} HSVA with components within [0..1]
+     */
+    hsva: function()
+    {
+        var hsla = this.hsla();
+        var h = hsla[0];
+        var s = hsla[1];
+        var l = hsla[2];
+
+        s *= l < 0.5 ? l : 1 - l;
+        return [h, s !== 0 ? 2 * s / (l + s) : 0, (l + s), hsla[3]];
+    },
+
+    /**
+     * @return {boolean}
+     */
+    hasAlpha: function()
+    {
+        return this._rgba[3] !== 1;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canBeShortHex: function()
+    {
+        if (this.hasAlpha())
+            return false;
+        for (var i = 0; i < 3; ++i) {
+            var c = Math.round(this._rgba[i] * 255);
+            if (c % 17)
+                return false;
+        }
+        return true;
+    },
+
+    /**
+     * @return {?string}
+     */
+    toString: function(format)
+    {
+        if (!format)
+            format = this._format;
+
+        /**
+         * @param {number} value
+         * @return {number}
+         */
+        function toRgbValue(value)
+        {
+            return Math.round(value * 255);
+        }
+
+        /**
+         * @param {number} value
+         * @return {string}
+         */
+        function toHexValue(value)
+        {
+            var hex = Math.round(value * 255).toString(16);
+            return hex.length === 1 ? "0" + hex : hex;
+        }
+
+        /**
+         * @param {number} value
+         * @return {string}
+         */
+        function toShortHexValue(value)
+        {
+            return (Math.round(value * 255) / 17).toString(16);
+        }
+
+        switch (format) {
+        case WebInspector.Color.Format.Original:
+            return this._originalText;
+        case WebInspector.Color.Format.RGB:
+            if (this.hasAlpha())
+                return null;
+            return String.sprintf("rgb(%d, %d, %d)", toRgbValue(this._rgba[0]), toRgbValue(this._rgba[1]), toRgbValue(this._rgba[2]));
+        case WebInspector.Color.Format.RGBA:
+            return String.sprintf("rgba(%d, %d, %d, %f)", toRgbValue(this._rgba[0]), toRgbValue(this._rgba[1]), toRgbValue(this._rgba[2]), this._rgba[3]);
+        case WebInspector.Color.Format.HSL:
+            if (this.hasAlpha())
+                return null;
+            var hsl = this.hsla();
+            return String.sprintf("hsl(%d, %d%, %d%)", Math.round(hsl[0] * 360), Math.round(hsl[1] * 100), Math.round(hsl[2] * 100));
+        case WebInspector.Color.Format.HSLA:
+            var hsla = this.hsla();
+            return String.sprintf("hsla(%d, %d%, %d%, %f)", Math.round(hsla[0] * 360), Math.round(hsla[1] * 100), Math.round(hsla[2] * 100), hsla[3]);
+        case WebInspector.Color.Format.HEX:
+            if (this.hasAlpha())
+                return null;
+            return String.sprintf("#%s%s%s", toHexValue(this._rgba[0]), toHexValue(this._rgba[1]), toHexValue(this._rgba[2])).toUpperCase();
+        case WebInspector.Color.Format.ShortHEX:
+            if (!this.canBeShortHex())
+                return null;
+            return String.sprintf("#%s%s%s", toShortHexValue(this._rgba[0]), toShortHexValue(this._rgba[1]), toShortHexValue(this._rgba[2])).toUpperCase();
+        case WebInspector.Color.Format.Nickname:
+            return this.nickname();
+        }
+
+        return this._originalText;
+    },
+
+    /**
+     * @return {Array.<number>}
+     */
+    _canonicalRGBA: function()
+    {
+        var rgba = new Array(3);
+        for (var i = 0; i < 3; ++i)
+            rgba[i] = Math.round(this._rgba[i] * 255);
+        if (this._rgba[3] !== 1)
+            rgba.push(this._rgba[3]);
+        return rgba;
+    },
+
+    /**
+     * @return {?string} nickname
+     */
+    nickname: function()
+    {
+        if (!WebInspector.Color._rgbaToNickname) {
+            WebInspector.Color._rgbaToNickname = {};
+            for (var nickname in WebInspector.Color.Nicknames) {
+                var rgba = WebInspector.Color.Nicknames[nickname];
+                WebInspector.Color._rgbaToNickname[rgba] = nickname;
+            }
+        }
+
+        return WebInspector.Color._rgbaToNickname[this._canonicalRGBA()] || null;
+    },
+
+    /**
+     * @return {DOMAgent.RGBA}
+     */
+    toProtocolRGBA: function()
+    {
+        var rgba = this._canonicalRGBA();
+        var result = { r: rgba[0], g: rgba[1], b: rgba[2] };
+        if (rgba[3] !== 1)
+            result.a = rgba[3];
+        return result;
+    }
+}
+
+/**
+ * @param {string} value
+ * return {number}
+ */
+WebInspector.Color._parseRgbNumeric = function(value)
+{
+    var parsed = parseInt(value, 10);
+    if (value.indexOf("%") !== -1)
+        parsed /= 100;
+    else
+        parsed /= 255;
+    return parsed;
+}
+
+/**
+ * @param {string} value
+ * return {number}
+ */
+WebInspector.Color._parseHueNumeric = function(value)
+{
+    return isNaN(value) ? 0 : (parseFloat(value) / 360) % 1;
+}
+
+/**
+ * @param {string} value
+ * return {number}
+ */
+WebInspector.Color._parseSatLightNumeric = function(value)
+{
+    return parseFloat(value) / 100;
+}
+
+/**
+ * @param {string} value
+ * return {number}
+ */
+WebInspector.Color._parseAlphaNumeric = function(value)
+{
+    return isNaN(value) ? 0 : parseFloat(value);
+}
+
+/**
+ * @param {Array.<number>} hsl
+ * @return {Array.<number>}
+ */
+WebInspector.Color._hsl2rgb = function(hsl)
+{
+    var h = hsl[0];
+    var s = hsl[1];
+    var l = hsl[2];
+
+    function hue2rgb(p, q, h)
+    {
+        if (h < 0)
+            h += 1;
+        else if (h > 1)
+            h -= 1;
+
+        if ((h * 6) < 1)
+            return p + (q - p) * h * 6;
+        else if ((h * 2) < 1)
+            return q;
+        else if ((h * 3) < 2)
+            return p + (q - p) * ((2 / 3) - h) * 6;
+        else
+            return p;
+    }
+
+    if (s < 0)
+        s = 0;
+
+    if (l <= 0.5)
+        var q = l * (1 + s);
+    else
+        var q = l + s - (l * s);
+
+    var p = 2 * l - q;
+
+    var tr = h + (1 / 3);
+    var tg = h;
+    var tb = h - (1 / 3);
+
+    var r = hue2rgb(p, q, tr);
+    var g = hue2rgb(p, q, tg);
+    var b = hue2rgb(p, q, tb);
+    return [r, g, b, hsl[3]];
+}
+
+WebInspector.Color.Nicknames = {
+    "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,20,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],
+    "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],
+    "darkTurquoise":      [0,206,209],
+    "darkViolet":         [148,0,211],
+    "deepPink":           [255,20,147],
+    "deepSkyBlue":        [0,191,255],
+    "dimGray":            [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],
+    "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],
+    "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],
+    "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],
+    "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],
+    "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],
+    "transparent":        [0, 0, 0, 0],
+};
+
+WebInspector.Color.PageHighlight = {
+    Content: WebInspector.Color.fromRGBA([111, 168, 220, .66]),
+    ContentLight: WebInspector.Color.fromRGBA([111, 168, 220, .5]),
+    ContentOutline: WebInspector.Color.fromRGBA([9, 83, 148]),
+    Padding: WebInspector.Color.fromRGBA([147, 196, 125, .55]),
+    PaddingLight: WebInspector.Color.fromRGBA([147, 196, 125, .4]),
+    Border: WebInspector.Color.fromRGBA([255, 229, 153, .66]),
+    BorderLight: WebInspector.Color.fromRGBA([255, 229, 153, .5]),
+    Margin: WebInspector.Color.fromRGBA([246, 178, 107, .66]),
+    MarginLight: WebInspector.Color.fromRGBA([246, 178, 107, .5]),
+    EventTarget: WebInspector.Color.fromRGBA([255, 196, 196, .66])
+}
+
+WebInspector.Color.Format = {
+    Original: "original",
+    Nickname: "nickname",
+    HEX: "hex",
+    ShortHEX: "shorthex",
+    RGB: "rgb",
+    RGBA: "rgba",
+    HSL: "hsl",
+    HSLA: "hsla"
+}
diff --git a/Source/devtools/front_end/CompilerScriptMapping.js b/Source/devtools/front_end/CompilerScriptMapping.js
new file mode 100644
index 0000000..e7bdcf4
--- /dev/null
+++ b/Source/devtools/front_end/CompilerScriptMapping.js
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.ScriptSourceMapping}
+ * @param {WebInspector.Workspace} workspace
+ * @param {WebInspector.SimpleWorkspaceProvider} networkWorkspaceProvider
+ */
+WebInspector.CompilerScriptMapping = function(workspace, networkWorkspaceProvider)
+{
+    this._workspace = workspace;
+    this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this);
+    this._networkWorkspaceProvider = networkWorkspaceProvider;
+    /** @type {Object.<string, WebInspector.SourceMap>} */
+    this._sourceMapForSourceMapURL = {};
+    /** @type {Object.<string, WebInspector.SourceMap>} */
+    this._sourceMapForScriptId = {};
+    this._scriptForSourceMap = new Map();
+    /** @type {Object.<string, WebInspector.SourceMap>} */
+    this._sourceMapForURL = {};
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
+}
+
+WebInspector.CompilerScriptMapping.prototype = {
+    /**
+     * @param {WebInspector.RawLocation} rawLocation
+     * @return {WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(rawLocation)
+    {
+        var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation);
+        var sourceMap = this._sourceMapForScriptId[debuggerModelLocation.scriptId];
+        var lineNumber = debuggerModelLocation.lineNumber;
+        var columnNumber = debuggerModelLocation.columnNumber || 0;
+        var entry = sourceMap.findEntry(lineNumber, columnNumber);
+        if (!entry || entry.length === 2)
+            return null;
+        var url = entry[2];
+        var uiSourceCode = this._workspace.uiSourceCodeForURL(url);
+        if (!uiSourceCode)
+            return null;
+        return new WebInspector.UILocation(uiSourceCode, entry[3], entry[4]);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.DebuggerModel.Location}
+     */
+    uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
+    {
+        if (!uiSourceCode.url)
+            return null;
+        var sourceMap = this._sourceMapForURL[uiSourceCode.url];
+        if (!sourceMap)
+            return null;
+        var entry = sourceMap.findEntryReversed(uiSourceCode.url, lineNumber);
+        return WebInspector.debuggerModel.createRawLocation(this._scriptForSourceMap.get(sourceMap), entry[0], entry[1]);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isIdentity: function()
+    {
+        return false;
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     */
+    addScript: function(script)
+    {
+        var sourceMap = this.loadSourceMapForScript(script);
+        if (!sourceMap)
+            return;
+
+        if (this._scriptForSourceMap.get(sourceMap)) {
+            this._sourceMapForScriptId[script.scriptId] = sourceMap;
+            script.pushSourceMapping(this);
+            return;
+        }
+
+        this._sourceMapForScriptId[script.scriptId] = sourceMap;
+        this._scriptForSourceMap.put(sourceMap, script);
+
+        var sourceURLs = sourceMap.sources();
+        for (var i = 0; i < sourceURLs.length; ++i) {
+            var sourceURL = sourceURLs[i];
+            if (this._sourceMapForURL[sourceURL])
+                continue;
+            this._sourceMapForURL[sourceURL] = sourceMap;
+            if (!this._workspace.hasMappingForURL(sourceURL) && !this._workspace.uiSourceCodeForURL(sourceURL)) {
+                var sourceContent = sourceMap.sourceContent(sourceURL);
+                var contentProvider;
+                if (sourceContent)
+                    contentProvider = new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Script, sourceContent);
+                else
+                    contentProvider = new WebInspector.CompilerSourceMappingContentProvider(sourceURL);
+                this._networkWorkspaceProvider.addFileForURL(sourceURL, contentProvider, true);
+            }
+            var uiSourceCode = this._workspace.uiSourceCodeForURL(sourceURL);
+            if (uiSourceCode) {
+                this._bindUISourceCode(uiSourceCode);
+                uiSourceCode.isContentScript = script.isContentScript;
+            }
+        }
+        script.pushSourceMapping(this);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _bindUISourceCode: function(uiSourceCode)
+    {
+        uiSourceCode.setSourceMapping(this);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _uiSourceCodeAddedToWorkspace: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        if (!uiSourceCode.url || !this._sourceMapForURL[uiSourceCode.url])
+            return;
+        this._bindUISourceCode(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     * @return {?WebInspector.SourceMap}
+     */
+    loadSourceMapForScript: function(script)
+    {
+        // script.sourceURL can be a random string, but is generally an absolute path -> complete it to inspected page url for
+        // relative links.
+        if (!script.sourceMapURL)
+            return null;
+        var scriptURL = WebInspector.ParsedURL.completeURL(WebInspector.inspectedPageURL, script.sourceURL);
+        if (!scriptURL)
+            return null;
+        var sourceMapURL = WebInspector.ParsedURL.completeURL(scriptURL, script.sourceMapURL);
+        if (!sourceMapURL)
+            return null;
+        var sourceMap = this._sourceMapForSourceMapURL[sourceMapURL];
+        if (sourceMap)
+            return sourceMap;
+
+        sourceMap = WebInspector.SourceMap.load(sourceMapURL, scriptURL);
+        if (!sourceMap)
+            return null;
+        this._sourceMapForSourceMapURL[sourceMapURL] = sourceMap;
+        return sourceMap;
+    },
+
+    _debuggerReset: function()
+    {
+        this._sourceMapForSourceMapURL = {};
+        this._sourceMapForScriptId = {};
+        this._scriptForSourceMap = new Map();
+        this._sourceMapForURL = {};
+    }
+}
diff --git a/Source/devtools/front_end/ConsoleMessage.js b/Source/devtools/front_end/ConsoleMessage.js
new file mode 100644
index 0000000..6bde349
--- /dev/null
+++ b/Source/devtools/front_end/ConsoleMessage.js
@@ -0,0 +1,937 @@
+/*
+ * Copyright (C) 2011 Google Inc.  All rights reserved.
+ * Copyright (C) 2007, 2008 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 Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.ConsoleMessage}
+ *
+ * @param {string} source
+ * @param {string} level
+ * @param {string} message
+ * @param {WebInspector.Linkifier} linkifier
+ * @param {string=} type
+ * @param {string=} url
+ * @param {number=} line
+ * @param {number=} repeatCount
+ * @param {Array.<RuntimeAgent.RemoteObject>=} parameters
+ * @param {ConsoleAgent.StackTrace=} stackTrace
+ * @param {NetworkAgent.RequestId=} requestId
+ * @param {boolean=} isOutdated
+ */
+WebInspector.ConsoleMessageImpl = function(source, level, message, linkifier, type, url, line, repeatCount, parameters, stackTrace, requestId, isOutdated)
+{
+    WebInspector.ConsoleMessage.call(this, source, level, url, line, repeatCount);
+
+    this._linkifier = linkifier;
+    this.type = type || WebInspector.ConsoleMessage.MessageType.Log;
+    this._messageText = message;
+    this._parameters = parameters;
+    this._stackTrace = stackTrace;
+    this._request = requestId ? WebInspector.networkLog.requestForId(requestId) : null;
+    this._isOutdated = isOutdated;
+    this._dataGrids = [];
+    this._dataGridParents = new Map();
+
+    this._customFormatters = {
+        "object": this._formatParameterAsObject,
+        "array":  this._formatParameterAsArray,
+        "node":   this._formatParameterAsNode,
+        "string": this._formatParameterAsString
+    };
+}
+
+WebInspector.ConsoleMessageImpl.prototype = {
+    wasShown: function()
+    {
+        for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
+            var dataGrid = this._dataGrids[i];
+            var parentElement = this._dataGridParents.get(dataGrid);
+            dataGrid.show(parentElement);
+        }
+    },
+
+    willHide: function()
+    {
+        for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
+            var dataGrid = this._dataGrids[i];
+            this._dataGridParents.put(dataGrid, dataGrid.element.parentElement);
+            dataGrid.detach();
+        }
+    },
+
+    _formatMessage: function()
+    {
+        this._formattedMessage = document.createElement("span");
+        this._formattedMessage.className = "console-message-text source-code";
+
+        if (this.source === WebInspector.ConsoleMessage.MessageSource.ConsoleAPI) {
+            switch (this.type) {
+                case WebInspector.ConsoleMessage.MessageType.Trace:
+                    this._messageElement = document.createTextNode("console.trace()");
+                    break;
+                case WebInspector.ConsoleMessage.MessageType.Clear:
+                    this._messageElement = document.createTextNode(WebInspector.UIString("Console was cleared"));
+                    this._formattedMessage.addStyleClass("console-info");
+                    break;
+                case WebInspector.ConsoleMessage.MessageType.Assert:
+                    var args = [WebInspector.UIString("Assertion failed:")];
+                    if (this._parameters)
+                        args = args.concat(this._parameters);
+                    this._messageElement = this._format(args);
+                    break;
+                case WebInspector.ConsoleMessage.MessageType.Dir:
+                    var obj = this._parameters ? this._parameters[0] : undefined;
+                    var args = ["%O", obj];
+                    this._messageElement = this._format(args);
+                    break;
+                case WebInspector.ConsoleMessage.MessageType.Profile:
+                    var title = WebInspector.ProfilesPanelDescriptor.resolveProfileTitle(this._messageText);
+                    this._messageElement = document.createTextNode(WebInspector.UIString("Profile '%s' started.", title));
+                    break;
+                case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
+                    var hashIndex = this._messageText.lastIndexOf("#");
+                    var title = WebInspector.ProfilesPanelDescriptor.resolveProfileTitle(this._messageText.substring(0, hashIndex));
+                    var uid = this._messageText.substring(hashIndex + 1);
+                    var format = WebInspector.UIString("Profile '%s' finished.", "%_");
+                    var link = WebInspector.linkifyURLAsNode("webkit-profile://CPU/" + uid, title);
+                    this._messageElement = document.createElement("span");
+                    this._formatWithSubstitutionString(format, [link], this._messageElement);
+                    break;
+                default:
+                    var args = this._parameters || [this._messageText];
+                    this._messageElement = this._format(args);
+            }
+        } else if (this.source === WebInspector.ConsoleMessage.MessageSource.Network) {
+            if (this._request) {
+                this._stackTrace = this._request.initiator.stackTrace;
+                if (this._request.initiator && this._request.initiator.url) {
+                    this.url = this._request.initiator.url;
+                    this.line = this._request.initiator.lineNumber;
+                }
+                this._messageElement = document.createElement("span");
+                if (this.level === WebInspector.ConsoleMessage.MessageLevel.Error) {
+                    this._messageElement.appendChild(document.createTextNode(this._request.requestMethod + " "));
+                    this._messageElement.appendChild(WebInspector.linkifyRequestAsNode(this._request));
+                    if (this._request.failed)
+                        this._messageElement.appendChild(document.createTextNode(" " + this._request.localizedFailDescription));
+                    else
+                        this._messageElement.appendChild(document.createTextNode(" " + this._request.statusCode + " (" + this._request.statusText + ")"));
+                } else {
+                    var fragment = WebInspector.linkifyStringAsFragmentWithCustomLinkifier(this._messageText, WebInspector.linkifyRequestAsNode.bind(null, this._request, ""));
+                    this._messageElement.appendChild(fragment);
+                }
+            } else {
+                if (this.url) {
+                    var isExternal = !WebInspector.resourceForURL(this.url);
+                    this._anchorElement = WebInspector.linkifyURLAsNode(this.url, this.url, "console-message-url", isExternal);
+                }
+                this._messageElement = this._format([this._messageText]);
+            }
+        } else {
+            var args = this._parameters || [this._messageText];
+            this._messageElement = this._format(args);
+        }
+
+        if (this.source !== WebInspector.ConsoleMessage.MessageSource.Network || this._request) {
+            if (this._stackTrace && this._stackTrace.length && this._stackTrace[0].url) {
+                this._anchorElement = this._linkifyCallFrame(this._stackTrace[0]);
+            } else if (this.url && this.url !== "undefined") {
+                this._anchorElement = this._linkifyLocation(this.url, this.line, 0);
+            }
+        }
+
+        this._formattedMessage.appendChild(this._messageElement);
+        if (this._anchorElement) {
+            this._formattedMessage.appendChild(document.createTextNode(" "));
+            this._formattedMessage.appendChild(this._anchorElement);
+        }
+        
+        var dumpStackTrace = !!this._stackTrace && this._stackTrace.length && (this.source === WebInspector.ConsoleMessage.MessageSource.Network || this.level === WebInspector.ConsoleMessage.MessageLevel.Error || this.type === WebInspector.ConsoleMessage.MessageType.Trace);
+        if (dumpStackTrace) {
+            var ol = document.createElement("ol");
+            ol.className = "outline-disclosure";
+            var treeOutline = new TreeOutline(ol);
+
+            var content = this._formattedMessage;
+            var root = new TreeElement(content, null, true);
+            content.treeElementForTest = root;
+            treeOutline.appendChild(root);
+            if (this.type === WebInspector.ConsoleMessage.MessageType.Trace)
+                root.expand();
+
+            this._populateStackTraceTreeElement(root);
+            this._formattedMessage = ol;
+        }
+
+        // This is used for inline message bubbles in SourceFrames, or other plain-text representations.
+        this._message = this._messageElement.textContent;
+    },
+
+    get message()
+    {
+        // force message formatting
+        var formattedMessage = this.formattedMessage;
+        return this._message;
+    },
+
+    get formattedMessage()
+    {
+        if (!this._formattedMessage)
+            this._formatMessage();
+        return this._formattedMessage;
+    },
+
+    /**
+     * @return {?WebInspector.NetworkRequest}
+     */
+    request: function()
+    {
+        return this._request;
+    },
+
+    _linkifyLocation: function(url, lineNumber, columnNumber)
+    {
+        // FIXME(62725): stack trace line/column numbers are one-based.
+        lineNumber = lineNumber ? lineNumber - 1 : 0;
+        columnNumber = columnNumber ? columnNumber - 1 : 0;
+        return this._linkifier.linkifyLocation(url, lineNumber, columnNumber, "console-message-url");
+    },
+
+    _linkifyCallFrame: function(callFrame)
+    {
+        return this._linkifyLocation(callFrame.url, callFrame.lineNumber, callFrame.columnNumber);
+    },
+
+    isErrorOrWarning: function()
+    {
+        return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
+    },
+
+    _format: function(parameters)
+    {
+        // This node is used like a Builder. Values are continually appended onto it.
+        var formattedResult = document.createElement("span");
+        if (!parameters.length)
+            return formattedResult;
+
+        // Formatting code below assumes that parameters are all wrappers whereas frontend console
+        // API allows passing arbitrary values as messages (strings, numbers, etc.). Wrap them here.
+        for (var i = 0; i < parameters.length; ++i) {
+            // FIXME: Only pass runtime wrappers here.
+            if (parameters[i] instanceof WebInspector.RemoteObject)
+                continue;
+
+            if (typeof parameters[i] === "object")
+                parameters[i] = WebInspector.RemoteObject.fromPayload(parameters[i]);
+            else
+                parameters[i] = WebInspector.RemoteObject.fromPrimitiveValue(parameters[i]);
+        }
+
+        // There can be string log and string eval result. We distinguish between them based on message type.
+        var shouldFormatMessage = WebInspector.RemoteObject.type(parameters[0]) === "string" && this.type !== WebInspector.ConsoleMessage.MessageType.Result;
+
+        // Multiple parameters with the first being a format string. Save unused substitutions.
+        if (shouldFormatMessage) {
+            // Multiple parameters with the first being a format string. Save unused substitutions.
+            var result = this._formatWithSubstitutionString(parameters[0].description, parameters.slice(1), formattedResult);
+            parameters = result.unusedSubstitutions;
+            if (parameters.length)
+                formattedResult.appendChild(document.createTextNode(" "));
+        }
+
+        if (this.type === WebInspector.ConsoleMessage.MessageType.Table) {
+            formattedResult.appendChild(this._formatParameterAsTable(parameters));
+            return formattedResult;
+        }
+
+        // Single parameter, or unused substitutions from above.
+        for (var i = 0; i < parameters.length; ++i) {
+            // Inline strings when formatting.
+            if (shouldFormatMessage && parameters[i].type === "string")
+                formattedResult.appendChild(document.createTextNode(parameters[i].description));
+            else
+                formattedResult.appendChild(this._formatParameter(parameters[i], false, true));
+            if (i < parameters.length - 1)
+                formattedResult.appendChild(document.createTextNode(" "));
+        }
+        return formattedResult;
+    },
+
+    /**
+     * @param {Object} output
+     * @param {boolean=} forceObjectFormat
+     * @param {boolean=} includePreview
+     */
+    _formatParameter: function(output, forceObjectFormat, includePreview)
+    {
+        var type;
+        if (forceObjectFormat)
+            type = "object";
+        else if (output instanceof WebInspector.RemoteObject)
+            type = output.subtype || output.type;
+        else
+            type = typeof output;
+
+        var formatter = this._customFormatters[type];
+        if (!formatter) {
+            formatter = this._formatParameterAsValue;
+            output = output.description;
+        }
+
+        var span = document.createElement("span");
+        span.className = "console-formatted-" + type + " source-code";
+        formatter.call(this, output, span, includePreview);
+        return span;
+    },
+
+    _formatParameterAsValue: function(val, elem)
+    {
+        elem.appendChild(document.createTextNode(val));
+    },
+
+    _formatParameterAsObject: function(obj, elem, includePreview)
+    {
+        this._formatParameterAsArrayOrObject(obj, obj.description, elem, includePreview);
+    },
+
+    /**
+     * @param {WebInspector.RemoteObject} obj
+     * @param {string} description
+     * @param {Element} elem
+     * @param {boolean} includePreview
+     */
+    _formatParameterAsArrayOrObject: function(obj, description, elem, includePreview)
+    {
+        var titleElement = document.createElement("span");
+        if (description)
+            titleElement.createTextChild(description);
+        if (includePreview && obj.preview) {
+            titleElement.addStyleClass("console-object-preview");
+            var lossless = this._appendObjectPreview(obj, description, titleElement);
+            if (lossless) {
+                elem.appendChild(titleElement);
+                return;
+            }
+        }
+        var section = new WebInspector.ObjectPropertiesSection(obj, titleElement);
+        section.enableContextMenu();
+        elem.appendChild(section.element);
+
+        var note = section.titleElement.createChild("span", "object-info-state-note");
+        note.title = WebInspector.UIString("Object state below is captured upon first expansion");
+    },
+
+    /**
+     * @param {WebInspector.RemoteObject} obj
+     * @param {string} description
+     * @param {Element} titleElement
+     * @return {boolean} true iff preview captured all information.
+     */
+    _appendObjectPreview: function(obj, description, titleElement)
+    {
+        var preview = obj.preview;
+        var isArray = obj.subtype === "array";
+
+        if (description)
+            titleElement.createTextChild(" ");
+        titleElement.createTextChild(isArray ? "[" : "{");
+        for (var i = 0; i < preview.properties.length; ++i) {
+            if (i > 0)
+                titleElement.createTextChild(", ");
+
+            var property = preview.properties[i];
+            if (!isArray || property.name != i) {
+                titleElement.createChild("span", "name").textContent = property.name;
+                titleElement.createTextChild(": ");
+            }
+
+            titleElement.appendChild(this._renderPropertyPreview(property));
+        }
+        if (preview.overflow)
+            titleElement.createChild("span").textContent = "\u2026";
+        titleElement.createTextChild(isArray ? "]" : "}");
+        return preview.lossless;
+    },
+
+    /**
+     * @param {RuntimeAgent.PropertyPreview} property
+     * @return {Element}
+     */
+    _renderPropertyPreview: function(property)
+    {
+        var span = document.createElement("span");
+        span.className = "console-formatted-" + property.type;
+
+        if (property.type === "function") {
+            span.textContent = "function";
+            return span;
+        }
+
+        if (property.type === "object" && property.subtype === "regexp") {
+            span.addStyleClass("console-formatted-string");
+            span.textContent = property.value;
+            return span;
+        }
+
+        if (property.type === "object" && property.subtype === "node" && property.value) {
+            span.addStyleClass("console-formatted-preview-node");
+            WebInspector.DOMPresentationUtils.createSpansForNodeTitle(span, property.value);
+            return span;
+        }
+
+        if (property.type === "string") {
+            span.textContent = "\"" + property.value + "\"";
+            return span;
+        }
+
+        span.textContent = property.value;
+        return span;
+    },
+
+    _formatParameterAsNode: function(object, elem)
+    {
+        function printNode(nodeId)
+        {
+            if (!nodeId) {
+                // Sometimes DOM is loaded after the sync message is being formatted, so we get no
+                // nodeId here. So we fall back to object formatting here.
+                this._formatParameterAsObject(object, elem, false);
+                return;
+            }
+            var treeOutline = new WebInspector.ElementsTreeOutline(false, false, true);
+            treeOutline.setVisible(true);
+            treeOutline.rootDOMNode = WebInspector.domAgent.nodeForId(nodeId);
+            treeOutline.element.addStyleClass("outline-disclosure");
+            if (!treeOutline.children[0].hasChildren)
+                treeOutline.element.addStyleClass("single-node");
+            elem.appendChild(treeOutline.element);
+            treeOutline.element.treeElementForTest = treeOutline.children[0];
+        }
+        object.pushNodeToFrontend(printNode.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.RemoteObject} array
+     * @return {boolean}
+     */
+    useArrayPreviewInFormatter: function(array)
+    {
+        return this.type !== WebInspector.ConsoleMessage.MessageType.DirXML && !!array.preview;
+    },
+
+    /**
+     * @param {WebInspector.RemoteObject} array
+     * @param {Element} elem
+     */
+    _formatParameterAsArray: function(array, elem)
+    {
+        if (this.useArrayPreviewInFormatter(array)) {
+            this._formatParameterAsArrayOrObject(array, "", elem, true);
+            return;
+        }
+
+        const maxFlatArrayLength = 100;
+        if (this._isOutdated || array.arrayLength() > maxFlatArrayLength)
+            this._formatParameterAsObject(array, elem, false);
+        else
+            array.getOwnProperties(this._printArray.bind(this, array, elem));
+    },
+
+    /**
+     * @param {Array.<WebInspector.RemoteObject>} parameters
+     * @return {Element}
+     */
+    _formatParameterAsTable: function(parameters)
+    {
+        var element = document.createElement("span");
+        var table = parameters[0];
+        if (!table || !table.preview)
+            return element;
+
+        var columnNames = [];
+        var preview = table.preview;
+        var rows = [];
+        for (var i = 0; i < preview.properties.length; ++i) {
+            var rowProperty = preview.properties[i];
+            var rowPreview = rowProperty.valuePreview;
+            if (!rowPreview)
+                continue;
+
+            var rowValue = {};
+            const maxColumnsToRender = 20;
+            for (var j = 0; j < rowPreview.properties.length && columnNames.length < maxColumnsToRender; ++j) {
+                var cellProperty = rowPreview.properties[j];
+                if (columnNames.indexOf(cellProperty.name) === -1)
+                    columnNames.push(cellProperty.name);
+                rowValue[cellProperty.name] = this._renderPropertyPreview(cellProperty);
+            }
+            rows.push([rowProperty.name, rowValue]);
+        }
+
+        var flatValues = [];
+        for (var i = 0; i < rows.length; ++i) {
+            var rowName = rows[i][0];
+            var rowValue = rows[i][1];
+            flatValues.push(rowName);
+            for (var j = 0; j < columnNames.length; ++j)
+                flatValues.push(rowValue[columnNames[j]]);
+        }
+
+        if (!flatValues.length)
+            return element;
+        columnNames.unshift(WebInspector.UIString("(index)"));
+        var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, flatValues);
+        dataGrid.renderInline();
+        this._dataGrids.push(dataGrid);
+        this._dataGridParents.put(dataGrid, element);
+        return element;
+    },
+
+    _formatParameterAsString: function(output, elem)
+    {
+        var span = document.createElement("span");
+        span.className = "console-formatted-string source-code";
+        span.appendChild(WebInspector.linkifyStringAsFragment(output.description));
+
+        // Make black quotes.
+        elem.removeStyleClass("console-formatted-string");
+        elem.appendChild(document.createTextNode("\""));
+        elem.appendChild(span);
+        elem.appendChild(document.createTextNode("\""));
+    },
+
+    _printArray: function(array, elem, properties)
+    {
+        if (!properties)
+            return;
+
+        var elements = [];
+        for (var i = 0; i < properties.length; ++i) {
+            var property = properties[i];
+            var name = property.name;
+            if (!isNaN(name))
+                elements[name] = this._formatAsArrayEntry(property.value);
+        }
+
+        elem.appendChild(document.createTextNode("["));
+        var lastNonEmptyIndex = -1;
+
+        function appendUndefined(elem, index)
+        {
+            if (index - lastNonEmptyIndex <= 1)
+                return;
+            var span = elem.createChild("span", "console-formatted-undefined");
+            span.textContent = WebInspector.UIString("undefined × %d", index - lastNonEmptyIndex - 1);
+        }
+
+        var length = array.arrayLength();
+        for (var i = 0; i < length; ++i) {
+            var element = elements[i];
+            if (!element)
+                continue;
+
+            if (i - lastNonEmptyIndex > 1) {
+                appendUndefined(elem, i);
+                elem.appendChild(document.createTextNode(", "));
+            }
+
+            elem.appendChild(element);
+            lastNonEmptyIndex = i;
+            if (i < length - 1)
+                elem.appendChild(document.createTextNode(", "));
+        }
+        appendUndefined(elem, length);
+
+        elem.appendChild(document.createTextNode("]"));
+    },
+
+    _formatAsArrayEntry: function(output)
+    {
+        // Prevent infinite expansion of cross-referencing arrays.
+        return this._formatParameter(output, output.subtype && output.subtype === "array", false);
+    },
+
+    _formatWithSubstitutionString: function(format, parameters, formattedResult)
+    {
+        var formatters = {};
+
+        function parameterFormatter(force, obj)
+        {
+            return this._formatParameter(obj, force, false);
+        }
+
+        function stringFormatter(obj)
+        {
+            return obj.description;
+        }
+
+        function floatFormatter(obj)
+        {
+            if (typeof obj.value !== "number")
+                return "NaN";
+            return obj.value;
+        }
+
+        function integerFormatter(obj)
+        {
+            if (typeof obj.value !== "number")
+                return "NaN";
+            return Math.floor(obj.value);
+        }
+
+        function bypassFormatter(obj)
+        {
+            return (obj instanceof Node) ? obj : "";
+        }
+
+        var currentStyle = null;
+        function styleFormatter(obj)
+        {
+            currentStyle = {};
+            var buffer = document.createElement("span");
+            buffer.setAttribute("style", obj.description);
+            for (var i = 0; i < buffer.style.length; i++) {
+                var property = buffer.style[i];
+                if (isWhitelistedProperty(property))
+                    currentStyle[property] = buffer.style[property];
+            }
+        }
+
+        function isWhitelistedProperty(property)
+        {
+            var prefixes = ["background", "border", "color", "font", "line", "margin", "padding", "text", "-webkit-background", "-webkit-border", "-webkit-font", "-webkit-margin", "-webkit-padding", "-webkit-text"];
+            for (var i = 0; i < prefixes.length; i++) {
+                if (property.startsWith(prefixes[i]))
+                    return true;
+            }
+            return false;
+        }
+
+        // Firebug uses %o for formatting objects.
+        formatters.o = parameterFormatter.bind(this, false);
+        formatters.s = stringFormatter;
+        formatters.f = floatFormatter;
+        // Firebug allows both %i and %d for formatting integers.
+        formatters.i = integerFormatter;
+        formatters.d = integerFormatter;
+
+        // Firebug uses %c for styling the message.
+        formatters.c = styleFormatter;
+
+        // Support %O to force object formatting, instead of the type-based %o formatting.
+        formatters.O = parameterFormatter.bind(this, true);
+
+        formatters._ = bypassFormatter;
+
+        function append(a, b)
+        {
+            if (b instanceof Node)
+                a.appendChild(b);
+            else if (typeof b !== "undefined") {
+                var toAppend = WebInspector.linkifyStringAsFragment(String(b));
+                if (currentStyle) {
+                    var wrapper = document.createElement('span');
+                    for (var key in currentStyle)
+                        wrapper.style[key] = currentStyle[key];
+                    wrapper.appendChild(toAppend);
+                    toAppend = wrapper;
+                }
+                a.appendChild(toAppend);
+            }
+            return a;
+        }
+
+        // String.format does treat formattedResult like a Builder, result is an object.
+        return String.format(format, parameters, formatters, formattedResult, append);
+    },
+
+    clearHighlight: function()
+    {
+        if (!this._formattedMessage)
+            return;
+
+        var highlightedMessage = this._formattedMessage;
+        delete this._formattedMessage;
+        delete this._anchorElement;
+        delete this._messageElement;
+        this._formatMessage();
+        this._element.replaceChild(this._formattedMessage, highlightedMessage);
+    },
+
+    highlightSearchResults: function(regexObject)
+    {
+        if (!this._formattedMessage)
+            return;
+
+        this._highlightSearchResultsInElement(regexObject, this._messageElement);
+        if (this._anchorElement)
+            this._highlightSearchResultsInElement(regexObject, this._anchorElement);
+
+        this._element.scrollIntoViewIfNeeded();
+    },
+
+    _highlightSearchResultsInElement: function(regexObject, element)
+    {
+        regexObject.lastIndex = 0;
+        var text = element.textContent;
+        var match = regexObject.exec(text);
+        var matchRanges = [];
+        while (match) {
+            matchRanges.push({ offset: match.index, length: match[0].length });
+            match = regexObject.exec(text);
+        }
+        WebInspector.highlightSearchResults(element, matchRanges);
+    },
+
+    matchesRegex: function(regexObject)
+    {
+        return regexObject.test(this._message) || (this._anchorElement && regexObject.test(this._anchorElement.textContent));
+    },
+
+    toMessageElement: function()
+    {
+        if (this._element)
+            return this._element;
+
+        var element = document.createElement("div");
+        element.message = this;
+        element.className = "console-message";
+
+        this._element = element;
+
+        switch (this.level) {
+        case WebInspector.ConsoleMessage.MessageLevel.Log:
+            element.addStyleClass("console-log-level");
+            break;
+        case WebInspector.ConsoleMessage.MessageLevel.Debug:
+            element.addStyleClass("console-debug-level");
+            break;
+        case WebInspector.ConsoleMessage.MessageLevel.Warning:
+            element.addStyleClass("console-warning-level");
+            break;
+        case WebInspector.ConsoleMessage.MessageLevel.Error:
+            element.addStyleClass("console-error-level");
+            break;
+        }
+
+        if (this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
+            element.addStyleClass("console-group-title");
+
+        element.appendChild(this.formattedMessage);
+
+        if (this.repeatCount > 1)
+            this.updateRepeatCount();
+
+        return element;
+    },
+
+    _populateStackTraceTreeElement: function(parentTreeElement)
+    {
+        for (var i = 0; i < this._stackTrace.length; i++) {
+            var frame = this._stackTrace[i];
+
+            var content = document.createElement("div");
+            var messageTextElement = document.createElement("span");
+            messageTextElement.className = "console-message-text source-code";
+            var functionName = frame.functionName || WebInspector.UIString("(anonymous function)");
+            messageTextElement.appendChild(document.createTextNode(functionName));
+            content.appendChild(messageTextElement);
+
+            if (frame.url) {
+                content.appendChild(document.createTextNode(" "));
+                var urlElement = this._linkifyCallFrame(frame);
+                content.appendChild(urlElement);
+            }
+
+            var treeElement = new TreeElement(content);
+            parentTreeElement.appendChild(treeElement);
+        }
+    },
+
+    updateRepeatCount: function() {
+        if (!this._element)
+            return;
+
+        if (!this.repeatCountElement) {
+            this.repeatCountElement = document.createElement("span");
+            this.repeatCountElement.className = "bubble";
+
+            this._element.insertBefore(this.repeatCountElement, this._element.firstChild);
+            this._element.addStyleClass("repeated-message");
+        }
+        this.repeatCountElement.textContent = this.repeatCount;
+    },
+
+    toString: function()
+    {
+        var sourceString;
+        switch (this.source) {
+            case WebInspector.ConsoleMessage.MessageSource.XML:
+                sourceString = "XML";
+                break;
+            case WebInspector.ConsoleMessage.MessageSource.JS:
+                sourceString = "JS";
+                break;
+            case WebInspector.ConsoleMessage.MessageSource.Network:
+                sourceString = "Network";
+                break;
+            case WebInspector.ConsoleMessage.MessageSource.ConsoleAPI:
+                sourceString = "ConsoleAPI";
+                break;
+            case WebInspector.ConsoleMessage.MessageSource.Storage:
+                sourceString = "Storage";
+                break;
+            case WebInspector.ConsoleMessage.MessageSource.AppCache:
+                sourceString = "AppCache";
+                break;
+            case WebInspector.ConsoleMessage.MessageSource.Rendering:
+                sourceString = "Rendering";
+                break;
+            case WebInspector.ConsoleMessage.MessageSource.CSS:
+                sourceString = "CSS";
+                break;
+            case WebInspector.ConsoleMessage.MessageSource.Security:
+                sourceString = "Security";
+                break;
+            case WebInspector.ConsoleMessage.MessageSource.Other:
+                sourceString = "Other";
+                break;
+        }
+
+        var typeString;
+        switch (this.type) {
+            case WebInspector.ConsoleMessage.MessageType.Log:
+                typeString = "Log";
+                break;
+            case WebInspector.ConsoleMessage.MessageType.Dir:
+                typeString = "Dir";
+                break;
+            case WebInspector.ConsoleMessage.MessageType.DirXML:
+                typeString = "Dir XML";
+                break;
+            case WebInspector.ConsoleMessage.MessageType.Trace:
+                typeString = "Trace";
+                break;
+            case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
+            case WebInspector.ConsoleMessage.MessageType.StartGroup:
+                typeString = "Start Group";
+                break;
+            case WebInspector.ConsoleMessage.MessageType.EndGroup:
+                typeString = "End Group";
+                break;
+            case WebInspector.ConsoleMessage.MessageType.Assert:
+                typeString = "Assert";
+                break;
+            case WebInspector.ConsoleMessage.MessageType.Result:
+                typeString = "Result";
+                break;
+            case WebInspector.ConsoleMessage.MessageType.Profile:
+            case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
+                typeString = "Profiling";
+                break;
+        }
+
+        var levelString;
+        switch (this.level) {
+            case WebInspector.ConsoleMessage.MessageLevel.Log:
+                levelString = "Log";
+                break;
+            case WebInspector.ConsoleMessage.MessageLevel.Warning:
+                levelString = "Warning";
+                break;
+            case WebInspector.ConsoleMessage.MessageLevel.Debug:
+                levelString = "Debug";
+                break;
+            case WebInspector.ConsoleMessage.MessageLevel.Error:
+                levelString = "Error";
+                break;
+        }
+
+        return sourceString + " " + typeString + " " + levelString + ": " + this.formattedMessage.textContent + "\n" + this.url + " line " + this.line;
+    },
+
+    get text()
+    {
+        return this._messageText;
+    },
+
+    location: function()
+    {
+        // FIXME(62725): stack trace line/column numbers are one-based.
+        var lineNumber = this.stackTrace ? this.stackTrace[0].lineNumber - 1 : this.line - 1;
+        var columnNumber = this.stackTrace && this.stackTrace[0].columnNumber ? this.stackTrace[0].columnNumber - 1 : 0;
+        return WebInspector.debuggerModel.createRawLocationByURL(this.url, lineNumber, columnNumber);
+    },
+
+    isEqual: function(msg)
+    {
+        if (!msg)
+            return false;
+
+        if (this._stackTrace) {
+            if (!msg._stackTrace)
+                return false;
+            var l = this._stackTrace;
+            var r = msg._stackTrace;
+            if (l.length !== r.length) 
+                return false;
+            for (var i = 0; i < l.length; i++) {
+                if (l[i].url !== r[i].url ||
+                    l[i].functionName !== r[i].functionName ||
+                    l[i].lineNumber !== r[i].lineNumber ||
+                    l[i].columnNumber !== r[i].columnNumber)
+                    return false;
+            }
+        }
+
+        return (this.source === msg.source)
+            && (this.type === msg.type)
+            && (this.level === msg.level)
+            && (this.line === msg.line)
+            && (this.url === msg.url)
+            && (this.message === msg.message)
+            && (this._request === msg._request);
+    },
+
+    get stackTrace()
+    {
+        return this._stackTrace;
+    },
+
+    /**
+     * @return {WebInspector.ConsoleMessage}
+     */
+    clone: function()
+    {
+        return WebInspector.ConsoleMessage.create(this.source, this.level, this._messageText, this.type, this.url, this.line, this.repeatCount, this._parameters, this._stackTrace, this._request ? this._request.requestId : undefined, this._isOutdated);
+    },
+
+    __proto__: WebInspector.ConsoleMessage.prototype
+}
diff --git a/Source/devtools/front_end/ConsoleModel.js b/Source/devtools/front_end/ConsoleModel.js
new file mode 100644
index 0000000..418b506
--- /dev/null
+++ b/Source/devtools/front_end/ConsoleModel.js
@@ -0,0 +1,311 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.ConsoleModel = function()
+{
+    this.messages = [];
+    this.warnings = 0;
+    this.errors = 0;
+    this._interruptRepeatCount = false;
+    InspectorBackend.registerConsoleDispatcher(new WebInspector.ConsoleDispatcher(this));
+}
+
+WebInspector.ConsoleModel.Events = {
+    ConsoleCleared: "console-cleared",
+    MessageAdded: "console-message-added",
+    RepeatCountUpdated: "repeat-count-updated"
+}
+
+WebInspector.ConsoleModel.prototype = {
+    enableAgent: function()
+    {
+        if (WebInspector.settings.monitoringXHREnabled.get())
+            ConsoleAgent.setMonitoringXHREnabled(true);
+
+        this._enablingConsole = true;
+        function callback()
+        {
+            delete this._enablingConsole;
+        }
+        ConsoleAgent.enable(callback.bind(this));
+    },
+
+    /**
+     * @return {boolean}
+     */
+    enablingConsole: function()
+    {
+        return !!this._enablingConsole;
+    },
+
+    /**
+     * @param {WebInspector.ConsoleMessage} msg
+     */
+    addMessage: function(msg)
+    {
+        this.messages.push(msg);
+        this._previousMessage = msg;
+        this._incrementErrorWarningCount(msg);
+        this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageAdded, msg);
+        this._interruptRepeatCount = false;
+    },
+
+    /**
+     * @param {WebInspector.ConsoleMessage} msg
+     */
+    _incrementErrorWarningCount: function(msg)
+    {
+        switch (msg.level) {
+            case WebInspector.ConsoleMessage.MessageLevel.Warning:
+                this.warnings += msg.repeatDelta;
+                break;
+            case WebInspector.ConsoleMessage.MessageLevel.Error:
+                this.errors += msg.repeatDelta;
+                break;
+        }
+    },
+
+    requestClearMessages: function()
+    {
+        ConsoleAgent.clearMessages();
+        this.clearMessages();
+    },
+
+    clearMessages: function()
+    {
+        this.messages = [];
+        delete this._previousMessage;
+
+        this.errors = 0;
+        this.warnings = 0;
+
+        this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.ConsoleCleared);
+    },
+
+    interruptRepeatCount: function()
+    {
+        this._interruptRepeatCount = true;
+    },
+
+    /**
+     * @param {number} count
+     */
+    _messageRepeatCountUpdated: function(count)
+    {
+        var msg = this._previousMessage;
+        if (!msg)
+            return;
+
+        var prevRepeatCount = msg.totalRepeatCount;
+
+        if (!this._interruptRepeatCount) {
+            msg.repeatDelta = count - prevRepeatCount;
+            msg.repeatCount = msg.repeatCount + msg.repeatDelta;
+            msg.totalRepeatCount = count;
+            msg.updateRepeatCount();
+
+            this._incrementErrorWarningCount(msg);
+            this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.RepeatCountUpdated, msg);
+        } else {
+            var msgCopy = msg.clone();
+            msgCopy.totalRepeatCount = count;
+            msgCopy.repeatCount = (count - prevRepeatCount) || 1;
+            msgCopy.repeatDelta = msgCopy.repeatCount;
+            this.addMessage(msgCopy);
+        }
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {string} source
+ * @param {string} level
+ * @param {string=} url
+ * @param {number=} line
+ * @param {number=} repeatCount
+ */
+WebInspector.ConsoleMessage = function(source, level, url, line, repeatCount)
+{
+    this.source = source;
+    this.level = level;
+    this.url = url || null;
+    this.line = line || 0;
+    this.message = "";
+
+    repeatCount = repeatCount || 1;
+    this.repeatCount = repeatCount;
+    this.repeatDelta = repeatCount;
+    this.totalRepeatCount = repeatCount;
+}
+
+WebInspector.ConsoleMessage.prototype = {
+    /**
+     * @return {boolean}
+     */
+    isErrorOrWarning: function()
+    {
+        return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
+    },
+
+    updateRepeatCount: function()
+    {
+        // Implemented by concrete instances
+    },
+
+    /**
+     * @return {WebInspector.ConsoleMessage}
+     */
+    clone: function()
+    {
+        // Implemented by concrete instances
+    },
+
+    /**
+     * @return {WebInspector.DebuggerModel.Location}
+     */
+    location: function()
+    {
+        // Implemented by concrete instances
+    }
+}
+
+/**
+ * @param {string} source
+ * @param {string} level
+ * @param {string} message
+ * @param {string=} type
+ * @param {string=} url
+ * @param {number=} line
+ * @param {number=} repeatCount
+ * @param {Array.<RuntimeAgent.RemoteObject>=} parameters
+ * @param {ConsoleAgent.StackTrace=} stackTrace
+ * @param {NetworkAgent.RequestId=} requestId
+ * @param {boolean=} isOutdated
+ * @return {WebInspector.ConsoleMessage}
+ */
+WebInspector.ConsoleMessage.create = function(source, level, message, type, url, line, repeatCount, parameters, stackTrace, requestId, isOutdated)
+{
+}
+
+// Note: Keep these constants in sync with the ones in Console.h
+WebInspector.ConsoleMessage.MessageSource = {
+    XML: "xml",
+    JS: "javascript",
+    Network: "network",
+    ConsoleAPI: "console-api",
+    Storage: "storage",
+    AppCache: "appcache",
+    Rendering: "rendering",
+    CSS: "css",
+    Security: "security",
+    Other: "other",
+    Deprecation: "deprecation"
+}
+
+WebInspector.ConsoleMessage.MessageType = {
+    Log: "log",
+    Dir: "dir",
+    DirXML: "dirxml",
+    Table: "table",
+    Trace: "trace",
+    Clear: "clear",
+    StartGroup: "startGroup",
+    StartGroupCollapsed: "startGroupCollapsed",
+    EndGroup: "endGroup",
+    Assert: "assert",
+    Result: "result",
+    Profile: "profile",
+    ProfileEnd: "profileEnd"
+}
+
+WebInspector.ConsoleMessage.MessageLevel = {
+    Log: "log",
+    Warning: "warning",
+    Error: "error",
+    Debug: "debug"
+}
+
+
+/**
+ * @constructor
+ * @implements {ConsoleAgent.Dispatcher}
+ * @param {WebInspector.ConsoleModel} console
+ */
+WebInspector.ConsoleDispatcher = function(console)
+{
+    this._console = console;
+}
+
+WebInspector.ConsoleDispatcher.prototype = {
+    /**
+     * @param {ConsoleAgent.ConsoleMessage} payload
+     */
+    messageAdded: function(payload)
+    {
+        var consoleMessage = WebInspector.ConsoleMessage.create(
+            payload.source,
+            payload.level,
+            payload.text,
+            payload.type,
+            payload.url,
+            payload.line,
+            payload.repeatCount,
+            payload.parameters,
+            payload.stackTrace,
+            payload.networkRequestId,
+            this._console._enablingConsole);
+        this._console.addMessage(consoleMessage);
+    },
+
+    /**
+     * @param {number} count
+     */
+    messageRepeatCountUpdated: function(count)
+    {
+        this._console._messageRepeatCountUpdated(count);
+    },
+
+    messagesCleared: function()
+    {
+        if (!WebInspector.settings.preserveConsoleLog.get())
+            this._console.clearMessages();
+    }
+}
+
+/**
+ * @type {?WebInspector.ConsoleModel}
+ */
+WebInspector.console = null;
diff --git a/Source/devtools/front_end/ConsolePanel.js b/Source/devtools/front_end/ConsolePanel.js
new file mode 100755
index 0000000..12778ac
--- /dev/null
+++ b/Source/devtools/front_end/ConsolePanel.js
@@ -0,0 +1,155 @@
+/*
+ * 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 Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Panel}
+ */
+WebInspector.ConsolePanel = function()
+{
+    WebInspector.Panel.call(this, "console");
+
+    WebInspector.consoleView.addEventListener(WebInspector.ConsoleView.Events.EntryAdded, this._consoleMessageAdded, this);
+    WebInspector.consoleView.addEventListener(WebInspector.ConsoleView.Events.ConsoleCleared, this._consoleCleared, this);
+    this._view = WebInspector.consoleView;
+}
+
+WebInspector.ConsolePanel.prototype = {
+    get statusBarItems()
+    {
+        return this._view.statusBarItems;
+    },
+
+    wasShown: function()
+    {
+        WebInspector.Panel.prototype.wasShown.call(this);
+        if (WebInspector.drawer.visible) {
+            WebInspector.drawer.hide(WebInspector.Drawer.AnimationType.Immediately);
+            this._drawerWasVisible = true;
+        }
+        this._view.show(this.element);
+    },
+
+    willHide: function()
+    {
+        if (this._drawerWasVisible) {
+            WebInspector.drawer.show(this._view, WebInspector.Drawer.AnimationType.Immediately);
+            delete this._drawerWasVisible;
+        }
+        WebInspector.Panel.prototype.willHide.call(this);
+    },
+
+    searchCanceled: function()
+    {
+        this._clearCurrentSearchResultHighlight();
+        delete this._searchResults;
+        delete this._searchRegex;
+    },
+
+    /**
+     * @param {string} query
+     */
+    performSearch: function(query)
+    {
+        WebInspector.searchController.updateSearchMatchesCount(0, this);
+        this.searchCanceled();
+        this._searchRegex = createPlainTextSearchRegex(query, "gi");
+
+        this._searchResults = [];
+        var messages = WebInspector.console.messages;
+        for (var i = 0; i < messages.length; i++) {
+            if (messages[i].matchesRegex(this._searchRegex)) {
+                this._searchResults.push(messages[i]);
+                this._searchRegex.lastIndex = 0;
+            }
+        }
+        WebInspector.searchController.updateSearchMatchesCount(this._searchResults.length, this);
+        this._currentSearchResultIndex = -1;
+        if (this._searchResults.length)
+            this._jumpToSearchResult(0);
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        this._jumpToSearchResult((this._currentSearchResultIndex + 1) % this._searchResults.length);
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        var index = this._currentSearchResultIndex - 1;
+        if (index === -1)
+            index = this._searchResults.length - 1;
+        this._jumpToSearchResult(index);
+    },
+
+    _clearCurrentSearchResultHighlight: function()
+    {
+        if (!this._searchResults)
+            return;
+        var highlightedMessage = this._searchResults[this._currentSearchResultIndex];
+        if (highlightedMessage)
+            highlightedMessage.clearHighlight();
+        this._currentSearchResultIndex = -1;
+    },
+
+    _jumpToSearchResult: function(index)
+    {
+        this._clearCurrentSearchResultHighlight();
+        this._currentSearchResultIndex = index;
+        WebInspector.searchController.updateCurrentMatchIndex(this._currentSearchResultIndex, this);
+        this._searchResults[index].highlightSearchResults(this._searchRegex);
+    },
+
+    _consoleMessageAdded: function(event)
+    {
+        if (!this._searchRegex || !this.isShowing())
+            return;
+        var message = event.data;
+        this._searchRegex.lastIndex = 0;
+        if (message.matchesRegex(this._searchRegex)) {
+            this._searchResults.push(message);
+            WebInspector.searchController.updateSearchMatchesCount(this._searchResults.length, this);
+        }
+    },
+
+    _consoleCleared: function()
+    {
+        if (!this._searchResults)
+            return;
+        this._clearCurrentSearchResultHighlight();
+        this._searchResults.length = 0;
+        if (this.isShowing())
+            WebInspector.searchController.updateSearchMatchesCount(0, this);
+    },
+
+    __proto__: WebInspector.Panel.prototype
+}
diff --git a/Source/devtools/front_end/ConsoleView.js b/Source/devtools/front_end/ConsoleView.js
new file mode 100644
index 0000000..8b1f61e
--- /dev/null
+++ b/Source/devtools/front_end/ConsoleView.js
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2007, 2008 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 Computer, 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.
+ */
+
+/**
+ * @extends {WebInspector.View}
+ * @constructor
+ * @param {boolean} hideContextSelector
+ */
+WebInspector.ConsoleView = function(hideContextSelector)
+{
+    WebInspector.View.call(this);
+
+    this.element.id = "console-view";
+    this._messageLevelFilters = {};
+    this._messageURLFilters = WebInspector.settings.messageURLFilters.get();
+    this._visibleMessages = [];
+    this._messages = [];
+    this._urlToMessageCount = {};
+
+    this._clearConsoleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear console log."), "clear-status-bar-item");
+    this._clearConsoleButton.addEventListener("click", this._requestClearMessages, this);
+
+    this._frameSelector = new WebInspector.StatusBarComboBox(this._frameChanged.bind(this), "console-context");
+    this._contextSelector = new WebInspector.StatusBarComboBox(this._contextChanged.bind(this), "console-context");
+
+    if (hideContextSelector) {
+        this._frameSelector.element.addStyleClass("hidden");
+        this._contextSelector.element.addStyleClass("hidden");
+    }
+
+    this.messagesElement = document.createElement("div");
+    this.messagesElement.id = "console-messages";
+    this.messagesElement.className = "monospace";
+    this.messagesElement.addEventListener("click", this._messagesClicked.bind(this), true);
+    this.element.appendChild(this.messagesElement);
+    this._scrolledToBottom = true;
+
+    this.promptElement = document.createElement("div");
+    this.promptElement.id = "console-prompt";
+    this.promptElement.className = "source-code";
+    this.promptElement.spellcheck = false;
+    this.messagesElement.appendChild(this.promptElement);
+    this.messagesElement.appendChild(document.createElement("br"));
+
+    this.topGroup = new WebInspector.ConsoleGroup(null);
+    this.messagesElement.insertBefore(this.topGroup.element, this.promptElement);
+    this.currentGroup = this.topGroup;
+
+    this._filterBarElement = document.createElement("div");
+    this._filterBarElement.className = "scope-bar status-bar-item";
+
+    function createDividerElement()
+    {
+        var dividerElement = document.createElement("div");
+        dividerElement.addStyleClass("scope-bar-divider");
+        this._filterBarElement.appendChild(dividerElement);
+    }
+
+    var updateFilterHandler = this._updateFilter.bind(this);
+
+    function createFilterElement(category, label)
+    {
+        var categoryElement = document.createElement("li");
+        categoryElement.category = category;
+        categoryElement.className = category;
+        categoryElement.addEventListener("click", updateFilterHandler, false);
+        categoryElement.textContent = label;
+
+        this._filterBarElement.appendChild(categoryElement);
+
+        return categoryElement;
+    }
+
+    this.allElement = createFilterElement.call(this, "all", WebInspector.UIString("All"));
+    createDividerElement.call(this);
+    this.errorElement = createFilterElement.call(this, "error", WebInspector.UIString("Errors"));
+    this.warningElement = createFilterElement.call(this, "warning", WebInspector.UIString("Warnings"));
+    this.logElement = createFilterElement.call(this, "log", WebInspector.UIString("Logs"));
+    this.debugElement = createFilterElement.call(this, "debug", WebInspector.UIString("Debug"));
+
+    this.filter(this.allElement, false);
+    this._registerShortcuts();
+    this.registerRequiredCSS("textPrompt.css");
+
+    this.messagesElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
+
+    WebInspector.settings.monitoringXHREnabled.addChangeListener(this._monitoringXHREnabledSettingChanged.bind(this));
+
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
+
+    this._linkifier = new WebInspector.Linkifier();
+
+    this.prompt = new WebInspector.TextPromptWithHistory(WebInspector.runtimeModel.completionsForTextPrompt.bind(WebInspector.runtimeModel));
+    this.prompt.setSuggestBoxEnabled("generic-suggest");
+    this.prompt.renderAsBlock();
+    this.prompt.attach(this.promptElement);
+    this.prompt.proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this), false);
+    this.prompt.setHistoryData(WebInspector.settings.consoleHistory.get());
+
+    WebInspector.runtimeModel.contextLists().forEach(this._addFrame, this);
+    WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListAdded, this._frameAdded, this);
+    WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListRemoved, this._frameRemoved, this);
+}
+
+WebInspector.ConsoleView.Events = {
+  ConsoleCleared: "console-cleared",
+  EntryAdded: "console-entry-added",
+}
+
+WebInspector.ConsoleView.prototype = {
+    get statusBarItems()
+    {
+        return [this._clearConsoleButton.element, this._frameSelector.element, this._contextSelector.element, this._filterBarElement];
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _frameAdded: function(event)
+    {
+        var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data);
+        this._addFrame(contextList);
+    },
+
+    /**
+     * @param {WebInspector.FrameExecutionContextList} contextList
+     */
+    _addFrame: function(contextList)
+    {
+        var option = this._frameSelector.createOption(contextList.displayName, contextList.url);
+        option._contextList = contextList;
+        contextList._consoleOption = option;
+        contextList.addEventListener(WebInspector.FrameExecutionContextList.EventTypes.ContextsUpdated, this._frameUpdated, this);
+        contextList.addEventListener(WebInspector.FrameExecutionContextList.EventTypes.ContextAdded, this._contextAdded, this);
+        this._frameChanged();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _frameRemoved: function(event)
+    {
+        var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data);
+        this._frameSelector.removeOption(contextList._consoleOption);
+        this._frameChanged();
+    },
+
+    _frameChanged: function()
+    {
+        var context = this._currentFrame();
+        if (!context) {
+            WebInspector.runtimeModel.setCurrentExecutionContext(null);
+            this._contextSelector.element.addStyleClass("hidden");
+            return;
+        }
+
+        var executionContexts = context.executionContexts();
+        if (executionContexts.length)
+            WebInspector.runtimeModel.setCurrentExecutionContext(executionContexts[0]);
+
+        if (executionContexts.length === 1) {
+            this._contextSelector.element.addStyleClass("hidden");
+            return;
+        }
+        this._contextSelector.element.removeStyleClass("hidden");
+        this._contextSelector.removeOptions();
+        for (var i = 0; i < executionContexts.length; i++)
+            this._appendContextOption(executionContexts[i]);
+    },
+
+    /**
+     * @param {WebInspector.ExecutionContext} executionContext
+     */
+    _appendContextOption: function(executionContext)
+    {
+        if (!WebInspector.runtimeModel.currentExecutionContext())
+            WebInspector.runtimeModel.setCurrentExecutionContext(executionContext);
+        var option = this._contextSelector.createOption(executionContext.name, executionContext.id);
+        option._executionContext = executionContext;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _contextChanged: function(event)
+    {
+        var option = this._contextSelector.selectedOption();
+        WebInspector.runtimeModel.setCurrentExecutionContext(option ? option._executionContext : null);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _frameUpdated: function(event)
+    {
+        var contextList = /** {WebInspector.FrameExecutionContextList */ event.data;
+        var option = contextList._consoleOption;
+        option.text = contextList.displayName;
+        option.title = contextList.url;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _contextAdded: function(event)
+    {
+        var contextList = /** {WebInspector.FrameExecutionContextList */ event.data;
+        if (contextList === this._currentFrame())
+            this._frameChanged();
+    },
+
+    /**
+     * @return {WebInspector.FrameExecutionContextList|undefined}
+     */
+    _currentFrame: function()
+    {
+        var option = this._frameSelector.selectedOption();
+        return option ? option._contextList : undefined;
+    },
+
+    _updateFilter: function(e)
+    {
+        var isMac = WebInspector.isMac();
+        var selectMultiple = false;
+        if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey)
+            selectMultiple = true;
+        if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey)
+            selectMultiple = true;
+
+        this.filter(e.target, selectMultiple);
+    },
+
+    filter: function(target, selectMultiple)
+    {
+        function unselectAll()
+        {
+            this._messageLevelFilters = {};
+
+            this.allElement.removeStyleClass("selected");
+            this.errorElement.removeStyleClass("selected");
+            this.warningElement.removeStyleClass("selected");
+            this.logElement.removeStyleClass("selected");
+            this.debugElement.removeStyleClass("selected");
+        }
+
+        if (target.category === "all") {
+            unselectAll.call(this);
+            target.addStyleClass("selected");
+            this._messageLevelFilters = {error: true, warning: true, log: true, debug: true};
+        } else {
+            // Something other than all is being selected, so we want to unselect all
+            if (this.allElement.hasStyleClass("selected")) {
+                this._messageLevelFilters = {};
+                this.allElement.removeStyleClass("selected");
+            }
+
+            if (!selectMultiple) {
+                // If multiple selection is off, we want to unselect everything else
+                // and just select ourselves.
+                unselectAll.call(this);
+
+                target.addStyleClass("selected");
+
+                this._messageLevelFilters = {};
+                this._messageLevelFilters[target.category] = true;
+            } else {
+
+                if (target.hasStyleClass("selected")) {
+                    // If selectMultiple is turned on, and we were selected, we just
+                    // want to unselect ourselves.
+                    target.removeStyleClass("selected");
+
+                    if (this._messageLevelFilters[target.category])
+                        delete this._messageLevelFilters[target.category];
+
+                } else {
+                    // If selectMultiple is turned on, and we weren't selected, we just
+                    // want to select ourselves.
+                    target.addStyleClass("selected");
+
+                    this._messageLevelFilters[target.category] = true;
+                }
+            }
+        }
+
+        this._updateMessageList();
+    },
+
+    willHide: function()
+    {
+        this.prompt.hideSuggestBox();
+        this.prompt.clearAutoComplete(true);
+    },
+
+    wasShown: function()
+    {
+        if (!this.prompt.isCaretInsidePrompt())
+            this.prompt.moveCaretToEndOfPrompt();
+    },
+
+    afterShow: function()
+    {
+        WebInspector.setCurrentFocusElement(this.promptElement);
+    },
+
+    storeScrollPositions: function()
+    {
+        WebInspector.View.prototype.storeScrollPositions.call(this);
+        this._scrolledToBottom = this.messagesElement.isScrolledToBottom();
+    },
+
+    restoreScrollPositions: function()
+    {
+        if (this._scrolledToBottom)
+            this._immediatelyScrollIntoView();
+        else
+            WebInspector.View.prototype.restoreScrollPositions.call(this);
+    },
+
+    onResize: function()
+    {
+        this.restoreScrollPositions();
+    },
+
+    _isScrollIntoViewScheduled: function()
+    {
+        return !!this._scrollIntoViewTimer;
+    },
+
+    _scheduleScrollIntoView: function()
+    {
+        if (this._scrollIntoViewTimer)
+            return;
+
+        function scrollIntoView()
+        {
+            delete this._scrollIntoViewTimer;
+            this.promptElement.scrollIntoView(true);
+        }
+        this._scrollIntoViewTimer = setTimeout(scrollIntoView.bind(this), 20);
+    },
+
+    _immediatelyScrollIntoView: function()
+    {
+        this.promptElement.scrollIntoView(true);
+        this._cancelScheduledScrollIntoView();
+    },
+
+    _cancelScheduledScrollIntoView: function()
+    {
+        if (!this._isScrollIntoViewScheduled())
+            return;
+
+        clearTimeout(this._scrollIntoViewTimer);
+        delete this._scrollIntoViewTimer;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _consoleMessageAdded: function(event)
+    {
+        var message = /** @type {WebInspector.ConsoleMessage} */ (event.data);
+        this._messages.push(message);
+
+        if (this._urlToMessageCount[message.url])
+            this._urlToMessageCount[message.url]++;
+        else
+            this._urlToMessageCount[message.url] = 1;
+
+        if (this._shouldBeVisible(message))
+            this._appendConsoleMessage(message);
+    },
+
+    _appendConsoleMessage: function(message)
+    {
+        // this.messagesElement.isScrolledToBottom() is forcing style recalculation.
+        // We just skip it if the scroll action has been scheduled.
+        if (!this._isScrollIntoViewScheduled() && ((message instanceof WebInspector.ConsoleCommandResult) || this.messagesElement.isScrolledToBottom()))
+            this._scheduleScrollIntoView();
+
+        this._visibleMessages.push(message);
+
+        if (message.type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
+            var parentGroup = this.currentGroup.parentGroup
+            if (parentGroup)
+                this.currentGroup = parentGroup;
+        } else {
+            if (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
+                var group = new WebInspector.ConsoleGroup(this.currentGroup);
+                this.currentGroup.messagesElement.appendChild(group.element);
+                this.currentGroup = group;
+                message.group = group;
+            }
+            this.currentGroup.addMessage(message);
+        }
+
+        this.dispatchEventToListeners(WebInspector.ConsoleView.Events.EntryAdded, message);
+    },
+
+    _consoleCleared: function()
+    {
+        this._scrolledToBottom = true;
+        for (var i = 0; i < this._visibleMessages.length; ++i)
+            this._visibleMessages[i].willHide();
+        this._visibleMessages = [];
+        this._messages = [];
+
+        this.currentGroup = this.topGroup;
+        this.topGroup.messagesElement.removeChildren();
+
+        this.dispatchEventToListeners(WebInspector.ConsoleView.Events.ConsoleCleared);
+
+        this._linkifier.reset();
+    },
+
+    _handleContextMenuEvent: function(event)
+    {
+        if (!window.getSelection().isCollapsed) {
+            // If there is a selection, we want to show our normal context menu
+            // (with Copy, etc.), and not Clear Console.
+            return;
+        }
+
+        if (event.target.enclosingNodeOrSelfWithNodeName("a"))
+            return;
+
+        var contextMenu = new WebInspector.ContextMenu(event);
+
+        function monitoringXHRItemAction()
+        {
+            WebInspector.settings.monitoringXHREnabled.set(!WebInspector.settings.monitoringXHREnabled.get());
+        }
+        contextMenu.appendCheckboxItem(WebInspector.UIString("Log XMLHttpRequests"), monitoringXHRItemAction.bind(this), WebInspector.settings.monitoringXHREnabled.get());
+
+        function preserveLogItemAction()
+        {
+            WebInspector.settings.preserveConsoleLog.set(!WebInspector.settings.preserveConsoleLog.get());
+        }
+        contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Preserve log upon navigation" : "Preserve Log upon Navigation"), preserveLogItemAction.bind(this), WebInspector.settings.preserveConsoleLog.get());
+
+        var sourceElement = event.target.enclosingNodeOrSelfWithClass("console-message");
+
+        var filterSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Filter"));
+
+        if (sourceElement && sourceElement.message.url)
+            filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Hide messages from %s" : "Hide Messages from %s", new WebInspector.ParsedURL(sourceElement.message.url).displayName), this._addMessageURLFilter.bind(this, sourceElement.message.url));
+
+        filterSubMenu.appendSeparator();
+        var unhideAll = filterSubMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Unhide all" : "Unhide All"), this._removeMessageURLFilter.bind(this));
+        filterSubMenu.appendSeparator();
+
+        var hasFilters = false;
+        for (var url in this._messageURLFilters) {
+            if (this._messageURLFilters.hasOwnProperty(url)) {
+                filterSubMenu.appendCheckboxItem(String.sprintf("%s (%d)", new WebInspector.ParsedURL(url).displayName, this._urlToMessageCount[url]), this._removeMessageURLFilter.bind(this, url), true);
+                hasFilters = true;
+            }
+        }
+
+        filterSubMenu.setEnabled(hasFilters || (sourceElement && sourceElement.message.url));
+        unhideAll.setEnabled(hasFilters);
+
+        contextMenu.appendSeparator();
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear console" : "Clear Console"), this._requestClearMessages.bind(this));
+
+        var messageElement = event.target.enclosingNodeOrSelfWithClass("console-message");
+        var request = (messageElement && messageElement.message) ? messageElement.message.request() : null;
+        if (request && request.type === WebInspector.resourceTypes.XHR) {
+            contextMenu.appendSeparator();
+            contextMenu.appendItem(WebInspector.UIString("Replay XHR"), NetworkAgent.replayXHR.bind(null, request.requestId));
+        }
+
+        contextMenu.show();
+    },
+
+    /**
+     * @param {string} url
+     * @private
+     */
+    _addMessageURLFilter: function(url)
+    {
+        this._messageURLFilters[url] = true;
+        WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
+        this._updateMessageList();
+    },
+
+    /**
+     * @param {string} url
+     * @private
+     */
+    _removeMessageURLFilter: function(url)
+    {
+        if (!url)
+            this._messageURLFilters = {};
+        else
+            delete this._messageURLFilters[url];
+
+        WebInspector.settings.messageURLFilters.set(this._messageURLFilters);
+
+        this._updateMessageList();
+    },
+
+    /**
+     * @param {WebInspector.ConsoleMessage} message
+     * @return {boolean}
+     * @private
+     */
+    _shouldBeVisible: function(message)
+    {
+        return (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed || message.type === WebInspector.ConsoleMessage.MessageType.EndGroup) ||
+            ((!message.url || !this._messageURLFilters[message.url]) && (!message.level || this._messageLevelFilters[message.level]));
+    },
+
+    /**
+     * @private
+     */
+    _updateMessageList: function()
+    {
+        var group = this.topGroup;
+        var sourceMessages = this._messages;
+        var visibleMessageIndex = 0;
+        var newVisibleMessages = [];
+        var anchor = null;
+        for (var i = 0; i < sourceMessages.length; i++) {
+            var sourceMessage = sourceMessages[i];
+            var visibleMessage = this._visibleMessages[visibleMessageIndex];
+
+            if (visibleMessage === sourceMessage) {
+                visibleMessageIndex++;
+                if (this._shouldBeVisible(visibleMessage)) {
+                    newVisibleMessages.push(visibleMessage);
+                    if (sourceMessage.type === WebInspector.ConsoleMessage.MessageType.EndGroup) {
+                        anchor = group.element;
+                        group = group.parentGroup || group;
+                    } else if (sourceMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroup || sourceMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
+                        group = sourceMessage.group;
+                        anchor = group.messagesElement.firstChild;
+                    }
+                } else {
+                    visibleMessage.willHide();
+                    visibleMessage.toMessageElement().removeSelf();
+                }
+            } else {
+                if (this._shouldBeVisible(sourceMessage)) {
+                    group.addMessage(sourceMessage, anchor ? anchor.nextSibling : group.messagesElement.firstChild);
+                    newVisibleMessages.push(sourceMessage);
+                    anchor = sourceMessage.toMessageElement();
+                }
+            }
+        }
+
+        this._visibleMessages = newVisibleMessages;
+    },
+
+    _monitoringXHREnabledSettingChanged: function(event)
+    {
+        ConsoleAgent.setMonitoringXHREnabled(event.data);
+    },
+
+    _messagesClicked: function(event)
+    {
+        if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
+            this.prompt.moveCaretToEndOfPrompt();
+    },
+
+    _registerShortcuts: function()
+    {
+        this._shortcuts = {};
+
+        var shortcut = WebInspector.KeyboardShortcut;
+        var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
+
+        var shortcutL = shortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
+        this._shortcuts[shortcutL.key] = this._requestClearMessages.bind(this);
+        var keys = [shortcutL];
+        if (WebInspector.isMac()) {
+            var shortcutK = shortcut.makeDescriptor("k", WebInspector.KeyboardShortcut.Modifiers.Meta);
+            this._shortcuts[shortcutK.key] = this._requestClearMessages.bind(this);
+            keys.unshift(shortcutK);
+        }
+        section.addAlternateKeys(keys, WebInspector.UIString("Clear console"));
+
+        section.addKey(shortcut.makeDescriptor(shortcut.Keys.Tab), WebInspector.UIString("Autocomplete common prefix"));
+        section.addKey(shortcut.makeDescriptor(shortcut.Keys.Right), WebInspector.UIString("Accept suggestion"));
+
+        keys = [
+            shortcut.makeDescriptor(shortcut.Keys.Down),
+            shortcut.makeDescriptor(shortcut.Keys.Up)
+        ];
+        section.addRelatedKeys(keys, WebInspector.UIString("Next/previous line"));
+
+        if (WebInspector.isMac()) {
+            keys = [
+                shortcut.makeDescriptor("N", shortcut.Modifiers.Alt),
+                shortcut.makeDescriptor("P", shortcut.Modifiers.Alt)
+            ];
+            section.addRelatedKeys(keys, WebInspector.UIString("Next/previous command"));
+        }
+
+        section.addKey(shortcut.makeDescriptor(shortcut.Keys.Enter), WebInspector.UIString("Execute command"));
+    },
+
+    _requestClearMessages: function()
+    {
+        WebInspector.console.requestClearMessages();
+    },
+
+    _promptKeyDown: function(event)
+    {
+        if (isEnterKey(event)) {
+            this._enterKeyPressed(event);
+            return;
+        }
+
+        var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
+        var handler = this._shortcuts[shortcut];
+        if (handler) {
+            handler();
+            event.preventDefault();
+            return;
+        }
+    },
+
+    evaluateUsingTextPrompt: function(expression, showResultOnly)
+    {
+        this._appendCommand(expression, this.prompt.text, false, showResultOnly);
+    },
+
+    _enterKeyPressed: function(event)
+    {
+        if (event.altKey || event.ctrlKey || event.shiftKey)
+            return;
+
+        event.consume(true);
+
+        this.prompt.clearAutoComplete(true);
+
+        var str = this.prompt.text;
+        if (!str.length)
+            return;
+        this._appendCommand(str, "", true, false);
+    },
+
+    _printResult: function(result, wasThrown, originatingCommand)
+    {
+        if (!result)
+            return;
+        var message = new WebInspector.ConsoleCommandResult(result, wasThrown, originatingCommand, this._linkifier);
+        this._messages.push(message);
+        this._appendConsoleMessage(message);
+    },
+
+    _appendCommand: function(text, newPromptText, useCommandLineAPI, showResultOnly)
+    {
+        if (!showResultOnly) {
+            var commandMessage = new WebInspector.ConsoleCommand(text);
+            WebInspector.console.interruptRepeatCount();
+            this._messages.push(commandMessage);
+            this._appendConsoleMessage(commandMessage);
+        }
+        this.prompt.text = newPromptText;
+
+        function printResult(result, wasThrown)
+        {
+            if (!result)
+                return;
+
+            if (!showResultOnly) {
+                this.prompt.pushHistoryItem(text);
+                WebInspector.settings.consoleHistory.set(this.prompt.historyData.slice(-30));
+            }
+            
+            this._printResult(result, wasThrown, commandMessage);
+        }
+        WebInspector.runtimeModel.evaluate(text, "console", useCommandLineAPI, false, false, true, printResult.bind(this));
+
+        WebInspector.userMetrics.ConsoleEvaluated.record();
+    },
+
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return [this.messagesElement];
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.ConsoleCommand = function(command)
+{
+    this.command = command;
+}
+
+WebInspector.ConsoleCommand.prototype = {
+    wasShown: function()
+    {
+    },
+
+    willHide: function()
+    {
+    },
+
+    clearHighlight: function()
+    {
+        var highlightedMessage = this._formattedCommand;
+        delete this._formattedCommand;
+        this._formatCommand();
+        this._element.replaceChild(this._formattedCommand, highlightedMessage);
+    },
+
+    highlightSearchResults: function(regexObject)
+    {
+        regexObject.lastIndex = 0;
+        var text = this.command;
+        var match = regexObject.exec(text);
+        var offset = 0;
+        var matchRanges = [];
+        while (match) {
+            matchRanges.push({ offset: match.index, length: match[0].length });
+            match = regexObject.exec(text);
+        }
+        WebInspector.highlightSearchResults(this._formattedCommand, matchRanges);
+        this._element.scrollIntoViewIfNeeded();
+    },
+
+    matchesRegex: function(regexObject)
+    {
+        return regexObject.test(this.command);
+    },
+
+    toMessageElement: function()
+    {
+        if (!this._element) {
+            this._element = document.createElement("div");
+            this._element.command = this;
+            this._element.className = "console-user-command";
+
+            this._formatCommand();
+            this._element.appendChild(this._formattedCommand);
+        }
+        return this._element;
+    },
+
+    _formatCommand: function()
+    {
+        this._formattedCommand = document.createElement("span");
+        this._formattedCommand.className = "console-message-text source-code";
+        this._formattedCommand.textContent = this.command;
+    },
+}
+
+/**
+ * @extends {WebInspector.ConsoleMessageImpl}
+ * @constructor
+ * @param {WebInspector.Linkifier} linkifier
+ */
+WebInspector.ConsoleCommandResult = function(result, wasThrown, originatingCommand, linkifier)
+{
+    var level = (wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log);
+    this.originatingCommand = originatingCommand;
+    WebInspector.ConsoleMessageImpl.call(this, WebInspector.ConsoleMessage.MessageSource.JS, level, "", linkifier, WebInspector.ConsoleMessage.MessageType.Result, undefined, undefined, undefined, [result]);
+}
+
+WebInspector.ConsoleCommandResult.prototype = {
+    /**
+     * @override
+     * @param {WebInspector.RemoteObject} array
+     * @return {boolean}
+     */
+    useArrayPreviewInFormatter: function(array)
+    {
+        return false;
+    },
+
+    toMessageElement: function()
+    {
+        var element = WebInspector.ConsoleMessageImpl.prototype.toMessageElement.call(this);
+        element.addStyleClass("console-user-command-result");
+        return element;
+    },
+
+    __proto__: WebInspector.ConsoleMessageImpl.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.ConsoleGroup = function(parentGroup)
+{
+    this.parentGroup = parentGroup;
+
+    var element = document.createElement("div");
+    element.className = "console-group";
+    element.group = this;
+    this.element = element;
+
+    if (parentGroup) {
+        var bracketElement = document.createElement("div");
+        bracketElement.className = "console-group-bracket";
+        element.appendChild(bracketElement);
+    }
+
+    var messagesElement = document.createElement("div");
+    messagesElement.className = "console-group-messages";
+    element.appendChild(messagesElement);
+    this.messagesElement = messagesElement;
+}
+
+WebInspector.ConsoleGroup.prototype = {
+    /**
+     * @param {WebInspector.ConsoleMessage} message
+     * @param {Node=} node
+     */
+    addMessage: function(message, node)
+    {
+        var element = message.toMessageElement();
+
+        if (message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
+            this.messagesElement.parentNode.insertBefore(element, this.messagesElement);
+            element.addEventListener("click", this._titleClicked.bind(this), false);
+            var groupElement = element.enclosingNodeOrSelfWithClass("console-group");
+            if (groupElement && message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
+                groupElement.addStyleClass("collapsed");
+        } else {
+            this.messagesElement.insertBefore(element, node || null);
+            message.wasShown();
+        }
+
+        if (element.previousSibling && message.originatingCommand && element.previousSibling.command === message.originatingCommand)
+            element.previousSibling.addStyleClass("console-adjacent-user-command-result");
+    },
+
+    _titleClicked: function(event)
+    {
+        var groupTitleElement = event.target.enclosingNodeOrSelfWithClass("console-group-title");
+        if (groupTitleElement) {
+            var groupElement = groupTitleElement.enclosingNodeOrSelfWithClass("console-group");
+            if (groupElement)
+                if (groupElement.hasStyleClass("collapsed"))
+                    groupElement.removeStyleClass("collapsed");
+                else
+                    groupElement.addStyleClass("collapsed");
+            groupTitleElement.scrollIntoViewIfNeeded(true);
+        }
+
+        event.consume(true);
+    }
+}
+
+/**
+ * @type {?WebInspector.ConsoleView}
+ */
+WebInspector.consoleView = null;
+
+WebInspector.ConsoleMessage.create = function(source, level, message, type, url, line, repeatCount, parameters, stackTrace, requestId, isOutdated)
+{
+    return new WebInspector.ConsoleMessageImpl(source, level, message, WebInspector.consoleView._linkifier, type, url, line, repeatCount, parameters, stackTrace, requestId, isOutdated);
+}
diff --git a/Source/devtools/front_end/ContentProvider.js b/Source/devtools/front_end/ContentProvider.js
new file mode 100644
index 0000000..c731aa2
--- /dev/null
+++ b/Source/devtools/front_end/ContentProvider.js
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.ContentProvider = function() { }
+
+WebInspector.ContentProvider.prototype = {
+    /**
+     * @return {string}
+     */
+    contentURL: function() { },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function() { },
+
+    /**
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestContent: function(callback) { },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback) { }
+}
+
+/**
+ * @constructor
+ * @param {number} lineNumber
+ * @param {string} lineContent
+ */
+WebInspector.ContentProvider.SearchMatch = function(lineNumber, lineContent) {
+    this.lineNumber = lineNumber;
+    this.lineContent = lineContent;
+}
+
+/**
+ * @param {string} content
+ * @param {string} query
+ * @param {boolean} caseSensitive
+ * @param {boolean} isRegex
+ * @return {Array.<WebInspector.ContentProvider.SearchMatch>}
+ */
+WebInspector.ContentProvider.performSearchInContent = function(content, query, caseSensitive, isRegex)
+{
+    var regex = createSearchRegex(query, caseSensitive, isRegex);
+
+    var result = [];
+    var lineEndings = content.lineEndings();
+    for (var i = 0; i < lineEndings.length; ++i) {
+        var lineStart = i > 0 ? lineEndings[i - 1] + 1 : 0;
+        var lineEnd = lineEndings[i];
+        var lineContent = content.substring(lineStart, lineEnd);
+        if (lineContent.length > 0 && lineContent.charAt(lineContent.length - 1) === "\r")
+            lineContent = lineContent.substring(0, lineContent.length - 1)
+
+        regex.lastIndex = 0;
+        if (regex.exec(lineContent))
+            result.push(new WebInspector.ContentProvider.SearchMatch(i, lineContent));
+    }
+    return result;
+}
diff --git a/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js b/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js
new file mode 100644
index 0000000..8f4897e
--- /dev/null
+++ b/Source/devtools/front_end/ContentProviderBasedProjectDelegate.js
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+ /**
+ * @constructor
+ * @implements {WebInspector.ProjectDelegate}
+ * @extends {WebInspector.Object}
+ * @param {string} type
+ */
+WebInspector.ContentProviderBasedProjectDelegate = function(type)
+{
+    this._type = type;
+    /** @type {Object.<string, WebInspector.ContentProvider>} */
+    this._contentProviders = {};
+}
+
+WebInspector.ContentProviderBasedProjectDelegate.prototype = {
+    /**
+     * @return {string}
+     */
+    id: function()
+    {
+        // Overriddden by subclasses
+        return "";
+    },
+
+    /**
+     * @return {string}
+     */
+    type: function()
+    {
+        return this._type;
+    },
+
+    /**
+     * @return {string}
+     */
+    displayName: function()
+    {
+        // Overriddden by subclasses
+        return "";
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestFileContent: function(path, callback)
+    {
+        var contentProvider = this._contentProviders[path.join("/")];
+        contentProvider.requestContent(callback);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canSetFileContent: function()
+    {
+        return false;
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {string} newContent
+     * @param {function(?string)} callback
+     */
+    setFileContent: function(path, newContent, callback)
+    {
+        callback(null);
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInFileContent: function(path, query, caseSensitive, isRegex, callback)
+    {
+        var contentProvider = this._contentProviders[path.join("/")];
+        contentProvider.searchInContent(query, caseSensitive, isRegex, callback);
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {string} url
+     * @param {WebInspector.ContentProvider} contentProvider
+     * @param {boolean} isEditable
+     * @param {boolean=} isContentScript
+     * @return {Array.<string>}
+     */
+    addContentProvider: function(path, url, contentProvider, isEditable, isContentScript)
+    {
+        var fileDescriptor = new WebInspector.FileDescriptor(path, url, url, contentProvider.contentType(), isEditable, isContentScript);
+        this._contentProviders[path.join("/")] = contentProvider;
+        this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.FileAdded, fileDescriptor);
+        return path;
+    },
+
+    /**
+     * @param {Array.<string>} path
+     */
+    removeFile: function(path)
+    {
+        delete this._contentProviders[path.join("/")];
+        this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.FileRemoved, path);
+    },
+
+    /**
+     * @return {Object.<string, WebInspector.ContentProvider>}
+     */
+    contentProviders: function()
+    {
+        return this._contentProviders;
+    },
+
+    reset: function()
+    {
+        this._contentProviders = {};
+        this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.Reset, null);
+    },
+    
+    __proto__: WebInspector.Object.prototype
+}
diff --git a/Source/devtools/front_end/ContentProviders.js b/Source/devtools/front_end/ContentProviders.js
new file mode 100644
index 0000000..6469600
--- /dev/null
+++ b/Source/devtools/front_end/ContentProviders.js
@@ -0,0 +1,298 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.ContentProvider}
+ * @param {Array.<WebInspector.Script>} scripts
+ */
+WebInspector.ConcatenatedScriptsContentProvider = function(scripts)
+{
+    this._mimeType = "text/html";
+    this._scripts = scripts;
+}
+
+WebInspector.ConcatenatedScriptsContentProvider.scriptOpenTag = "<script>";
+WebInspector.ConcatenatedScriptsContentProvider.scriptCloseTag = "</script>";
+
+WebInspector.ConcatenatedScriptsContentProvider.prototype = {
+    /**
+     * @return {Array.<WebInspector.Script>}
+     */
+    _sortedScripts: function()
+    {
+        if (this._sortedScriptsArray)
+            return this._sortedScriptsArray;
+
+        this._sortedScriptsArray = [];
+        
+        var scripts = this._scripts.slice();
+        scripts.sort(function(x, y) { return x.lineOffset - y.lineOffset || x.columnOffset - y.columnOffset; });
+        
+        var scriptOpenTagLength = WebInspector.ConcatenatedScriptsContentProvider.scriptOpenTag.length;
+        var scriptCloseTagLength = WebInspector.ConcatenatedScriptsContentProvider.scriptCloseTag.length;
+        
+        this._sortedScriptsArray.push(scripts[0]);
+        for (var i = 1; i < scripts.length; ++i) {
+            var previousScript = this._sortedScriptsArray[this._sortedScriptsArray.length - 1];
+            
+            var lineNumber = previousScript.endLine;
+            var columnNumber = previousScript.endColumn + scriptCloseTagLength + scriptOpenTagLength;
+            
+            if (lineNumber < scripts[i].lineOffset || (lineNumber === scripts[i].lineOffset && columnNumber <= scripts[i].columnOffset))
+                this._sortedScriptsArray.push(scripts[i]);
+        }
+        return this._sortedScriptsArray;
+    },
+
+    /**
+     * @return {string}
+     */
+    contentURL: function()
+    {
+        return "";
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function()
+    {
+        return WebInspector.resourceTypes.Document;
+    },
+    
+    /**
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestContent: function(callback)
+    {
+        var scripts = this._sortedScripts();
+        var sources = [];
+        function didRequestSource(content, contentEncoded, mimeType)
+        {
+            sources.push(content);
+            if (sources.length == scripts.length)
+                callback(this._concatenateScriptsContent(scripts, sources), false, this._mimeType);
+        }
+        for (var i = 0; i < scripts.length; ++i)
+            scripts[i].requestContent(didRequestSource.bind(this));
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback)
+    {
+        var results = {};
+        var scripts = this._sortedScripts();
+        var scriptsLeft = scripts.length;
+
+        function maybeCallback()
+        {
+            if (scriptsLeft)
+                return;
+
+            var result = [];
+            for (var i = 0; i < scripts.length; ++i)
+                result = result.concat(results[scripts[i].scriptId]);
+            callback(result);
+        }
+
+        /**
+         * @param {WebInspector.Script} script
+         * @param {Array.<PageAgent.SearchMatch>} searchMatches
+         */
+        function searchCallback(script, searchMatches)
+        {
+            results[script.scriptId] = [];
+            for (var i = 0; i < searchMatches.length; ++i) {
+                var searchMatch = new WebInspector.ContentProvider.SearchMatch(searchMatches[i].lineNumber + script.lineOffset, searchMatches[i].lineContent);
+                results[script.scriptId].push(searchMatch);
+            }
+            scriptsLeft--;
+            maybeCallback.call(this);
+        }
+
+        maybeCallback();
+        for (var i = 0; i < scripts.length; ++i)
+            scripts[i].searchInContent(query, caseSensitive, isRegex, searchCallback.bind(this, scripts[i]));
+    },
+
+    /**
+     * @return {string}
+     */
+    _concatenateScriptsContent: function(scripts, sources)
+    {
+        var content = "";
+        var lineNumber = 0;
+        var columnNumber = 0;
+
+        var scriptOpenTag = WebInspector.ConcatenatedScriptsContentProvider.scriptOpenTag;
+        var scriptCloseTag = WebInspector.ConcatenatedScriptsContentProvider.scriptCloseTag;
+        for (var i = 0; i < scripts.length; ++i) {
+            // Fill the gap with whitespace characters.
+            for (var newLinesCount = scripts[i].lineOffset - lineNumber; newLinesCount > 0; --newLinesCount) {
+                columnNumber = 0;
+                content += "\n";
+            }
+            for (var spacesCount = scripts[i].columnOffset - columnNumber - scriptOpenTag.length; spacesCount > 0; --spacesCount)
+                content += " ";
+
+            // Add script tag.
+            content += scriptOpenTag;
+            content += sources[i];
+            content += scriptCloseTag;
+            lineNumber = scripts[i].endLine;
+            columnNumber = scripts[i].endColumn + scriptCloseTag.length;
+        }
+
+        return content;
+    },
+
+    __proto__: WebInspector.ContentProvider.prototype
+}
+
+/**
+ * @constructor
+ * @param {string} sourceURL
+ * @implements {WebInspector.ContentProvider}
+ */
+WebInspector.CompilerSourceMappingContentProvider = function(sourceURL)
+{
+    this._sourceURL = sourceURL;
+}
+
+WebInspector.CompilerSourceMappingContentProvider.prototype = {
+    /**
+     * @return {string}
+     */
+    contentURL: function()
+    {
+        return this._sourceURL;
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function()
+    {
+        return WebInspector.resourceTypes.Script;
+    },
+    
+    /**
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestContent: function(callback)
+    {
+        var sourceCode = "";
+        try {
+            // FIXME: make sendRequest async.
+            sourceCode = InspectorFrontendHost.loadResourceSynchronously(this._sourceURL);
+        } catch(e) {
+            console.error(e.message);
+        }
+        callback(sourceCode, false, "text/javascript");
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback)
+    {
+        callback([]);
+    },
+
+    __proto__: WebInspector.ContentProvider.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ContentProvider}
+ * @param {WebInspector.ResourceType} contentType 
+ * @param {string} content
+ * @param {string=} mimeType
+ */
+WebInspector.StaticContentProvider = function(contentType, content, mimeType)
+{
+    this._content = content;
+    this._contentType = contentType;
+    this._mimeType = mimeType;
+}
+
+WebInspector.StaticContentProvider.prototype = {
+    /**
+     * @return {string}
+     */
+    contentURL: function()
+    {
+        return "";
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function()
+    {
+        return this._contentType;
+    },
+
+    /**
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestContent: function(callback)
+    {
+        callback(this._content, false, this._mimeType || this._contentType.canonicalMimeType());
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback)
+    {
+        function performSearch()
+        {
+            callback(WebInspector.ContentProvider.performSearchInContent(this._content, query, caseSensitive, isRegex));
+        }
+
+        // searchInContent should call back later.
+        window.setTimeout(performSearch.bind(this), 0);
+    },
+
+    __proto__: WebInspector.ContentProvider.prototype
+}
diff --git a/Source/devtools/front_end/ContextMenu.js b/Source/devtools/front_end/ContextMenu.js
new file mode 100644
index 0000000..98bf1a4
--- /dev/null
+++ b/Source/devtools/front_end/ContextMenu.js
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.ContextSubMenuItem} topLevelMenu
+ * @param {string} type
+ * @param {string=} label
+ * @param {boolean=} disabled
+ * @param {boolean=} checked
+ */
+WebInspector.ContextMenuItem = function(topLevelMenu, type, label, disabled, checked)
+{
+    this._type = type;
+    this._label = label;
+    this._disabled = disabled;
+    this._checked = checked;
+    this._contextMenu = topLevelMenu;
+    if (type === "item" || type === "checkbox")
+        this._id = topLevelMenu.nextId();
+}
+
+WebInspector.ContextMenuItem.prototype = {
+    id: function()
+    {
+        return this._id;
+    },
+
+    type: function()
+    {
+        return this._type;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isEnabled: function()
+    {
+        return !this._disabled;
+    },
+
+    /**
+     * @param {boolean} enabled
+     */
+    setEnabled: function(enabled)
+    {
+        this._disabled = !enabled;
+    },
+
+    _buildDescriptor: function()
+    {
+        switch (this._type) {
+        case "item":
+            return { type: "item", id: this._id, label: this._label, enabled: !this._disabled };
+        case "separator":
+            return { type: "separator" };
+        case "checkbox":
+            return { type: "checkbox", id: this._id, label: this._label, checked: !!this._checked, enabled: !this._disabled };
+        }
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ContextMenuItem}
+ * @param topLevelMenu
+ * @param {string=} label
+ * @param {boolean=} disabled
+ */
+WebInspector.ContextSubMenuItem = function(topLevelMenu, label, disabled)
+{
+    WebInspector.ContextMenuItem.call(this, topLevelMenu, "subMenu", label, disabled);
+    /** @type {!Array.<!WebInspector.ContextMenuItem>} */
+    this._items = [];
+}
+
+WebInspector.ContextSubMenuItem.prototype = {
+    /**
+     * @param {string} label
+     * @param {function(?)} handler
+     * @param {boolean=} disabled
+     * @return {WebInspector.ContextMenuItem}
+     */
+    appendItem: function(label, handler, disabled)
+    {
+        var item = new WebInspector.ContextMenuItem(this._contextMenu, "item", label, disabled);
+        this._pushItem(item);
+        this._contextMenu._setHandler(item.id(), handler);
+        return item;
+    },
+
+    /**
+     * @param {string} label
+     * @param {boolean=} disabled
+     * @return {WebInspector.ContextMenuItem}
+     */
+    appendSubMenuItem: function(label, disabled)
+    {
+        var item = new WebInspector.ContextSubMenuItem(this._contextMenu, label, disabled);
+        this._pushItem(item);
+        return item;
+    },
+
+    /**
+     * @param {boolean=} disabled
+     */
+    appendCheckboxItem: function(label, handler, checked, disabled)
+    {
+        var item = new WebInspector.ContextMenuItem(this._contextMenu, "checkbox", label, disabled, checked);
+        this._pushItem(item);
+        this._contextMenu._setHandler(item.id(), handler);
+        return item;
+    },
+
+    appendSeparator: function()
+    {
+        if (this._items.length)
+            this._pendingSeparator = true;
+    },
+
+    /**
+     * @param {!WebInspector.ContextMenuItem} item
+     */
+    _pushItem: function(item)
+    {
+        if (this._pendingSeparator) {
+            this._items.push(new WebInspector.ContextMenuItem(this._contextMenu, "separator"));
+            delete this._pendingSeparator;
+        }
+        this._items.push(item);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isEmpty: function()
+    {
+        return !this._items.length;
+    },
+
+    _buildDescriptor: function()
+    {
+        var result = { type: "subMenu", label: this._label, enabled: !this._disabled, subItems: [] };
+        for (var i = 0; i < this._items.length; ++i)
+            result.subItems.push(this._items[i]._buildDescriptor());
+        return result;
+    },
+
+    __proto__: WebInspector.ContextMenuItem.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ContextSubMenuItem}
+ */
+WebInspector.ContextMenu = function(event) {
+    WebInspector.ContextSubMenuItem.call(this, this, "");
+    this._event = event;
+    this._handlers = {};
+    this._id = 0;
+}
+
+WebInspector.ContextMenu.prototype = {
+    nextId: function()
+    {
+        return this._id++;
+    },
+
+    show: function()
+    {
+        var menuObject = this._buildDescriptor();
+
+        if (menuObject.length) {
+            WebInspector._contextMenu = this;
+            InspectorFrontendHost.showContextMenu(this._event, menuObject);
+            this._event.consume();
+        }
+    },
+
+    showSoftMenu: function()
+    {
+        var menuObject = this._buildDescriptor();
+
+        if (menuObject.length) {
+            WebInspector._contextMenu = this;
+            var softMenu = new WebInspector.SoftContextMenu(menuObject);
+            softMenu.show(this._event, true);
+        }
+        this._event.consume();
+    },
+
+    _setHandler: function(id, handler)
+    {
+        if (handler)
+            this._handlers[id] = handler;
+    },
+
+    _buildDescriptor: function()
+    {
+        var result = [];
+        for (var i = 0; i < this._items.length; ++i)
+            result.push(this._items[i]._buildDescriptor());
+        return result;
+    },
+
+    _itemSelected: function(id)
+    {
+        if (this._handlers[id])
+            this._handlers[id].call(this);
+    },
+
+    /**
+     * @param {Object} target
+     */
+    appendApplicableItems: function(target)
+    {
+        for (var i = 0; i < WebInspector.ContextMenu._providers.length; ++i) {
+            var provider = WebInspector.ContextMenu._providers[i];
+            this.appendSeparator();
+            provider.appendApplicableItems(this._event, this, target);
+            this.appendSeparator();
+        }
+    },
+
+    __proto__: WebInspector.ContextSubMenuItem.prototype
+}
+
+/**
+ * @interface
+ */
+WebInspector.ContextMenu.Provider = function() { 
+}
+
+WebInspector.ContextMenu.Provider.prototype = {
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    appendApplicableItems: function(event, contextMenu, target) { }
+}
+
+/**
+ * @param {WebInspector.ContextMenu.Provider} provider
+ */
+WebInspector.ContextMenu.registerProvider = function(provider)
+{
+    WebInspector.ContextMenu._providers.push(provider);
+}
+
+WebInspector.ContextMenu._providers = [];
+
+WebInspector.contextMenuItemSelected = function(id)
+{
+    if (WebInspector._contextMenu)
+        WebInspector._contextMenu._itemSelected(id);
+}
+
+WebInspector.contextMenuCleared = function()
+{
+    // FIXME: Unfortunately, contextMenuCleared is invoked between show and item selected
+    // so we can't delete last menu object from WebInspector. Fix the contract.
+}
diff --git a/Source/devtools/front_end/CookieItemsView.js b/Source/devtools/front_end/CookieItemsView.js
new file mode 100644
index 0000000..0fddf9e
--- /dev/null
+++ b/Source/devtools/front_end/CookieItemsView.js
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2009 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 Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.CookieItemsView = function(treeElement, cookieDomain)
+{
+    WebInspector.View.call(this);
+
+    this.element.addStyleClass("storage-view");
+
+    this._deleteButton = new WebInspector.StatusBarButton(WebInspector.UIString("Delete"), "delete-storage-status-bar-item");
+    this._deleteButton.visible = false;
+    this._deleteButton.addEventListener("click", this._deleteButtonClicked, this);
+
+    this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-storage-status-bar-item");
+    this._clearButton.visible = false;
+    this._clearButton.addEventListener("click", this._clearButtonClicked, this);
+
+    this._refreshButton = new WebInspector.StatusBarButton(WebInspector.UIString("Refresh"), "refresh-storage-status-bar-item");
+    this._refreshButton.addEventListener("click", this._refreshButtonClicked, this);
+
+    this._treeElement = treeElement;
+    this._cookieDomain = cookieDomain;
+
+    this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("This site has no cookies."));
+    this._emptyView.show(this.element);
+
+    this.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
+}
+
+WebInspector.CookieItemsView.prototype = {
+    get statusBarItems()
+    {
+        return [this._refreshButton.element, this._clearButton.element, this._deleteButton.element];
+    },
+
+    wasShown: function()
+    {
+        this._update();
+    },
+
+    willHide: function()
+    {
+        this._deleteButton.visible = false;
+    },
+
+    _update: function()
+    {
+        WebInspector.Cookies.getCookiesAsync(this._updateWithCookies.bind(this));
+    },
+
+    /**
+     * @param {Array.<WebInspector.Cookie>} allCookies
+     */
+    _updateWithCookies: function(allCookies)
+    {
+        this._cookies = this._filterCookiesForDomain(allCookies);
+
+        if (!this._cookies.length) {
+            // Nothing to show.
+            this._emptyView.show(this.element);
+            this._clearButton.visible = false;
+            this._deleteButton.visible = false;
+            if (this._cookiesTable)
+                this._cookiesTable.detach();
+            return;
+        }
+
+        if (!this._cookiesTable)
+            this._cookiesTable = new WebInspector.CookiesTable(false, this._update.bind(this), this._showDeleteButton.bind(this));
+
+        this._cookiesTable.setCookies(this._cookies);
+        this._emptyView.detach();
+        this._cookiesTable.show(this.element);
+        this._treeElement.subtitle = String.sprintf(WebInspector.UIString("%d cookies (%s)"), this._cookies.length,
+            Number.bytesToString(this._totalSize));
+        this._clearButton.visible = true;
+        this._deleteButton.visible = !!this._cookiesTable.selectedCookie();
+    },
+
+    /**
+     * @param {Array.<WebInspector.Cookie>} allCookies
+     */
+    _filterCookiesForDomain: function(allCookies)
+    {
+        var cookies = [];
+        var resourceURLsForDocumentURL = [];
+        this._totalSize = 0;
+
+        function populateResourcesForDocuments(resource)
+        {
+            var url = resource.documentURL.asParsedURL();
+            if (url && url.host == this._cookieDomain)
+                resourceURLsForDocumentURL.push(resource.url);
+        }
+        WebInspector.forAllResources(populateResourcesForDocuments.bind(this));
+
+        for (var i = 0; i < allCookies.length; ++i) {
+            var pushed = false;
+            var size = allCookies[i].size();
+            for (var j = 0; j < resourceURLsForDocumentURL.length; ++j) {
+                var resourceURL = resourceURLsForDocumentURL[j];
+                if (WebInspector.Cookies.cookieMatchesResourceURL(allCookies[i], resourceURL)) {
+                    this._totalSize += size;
+                    if (!pushed) {
+                        pushed = true;
+                        cookies.push(allCookies[i]);
+                    }
+                }
+            }
+        }
+        return cookies;
+    },
+
+    clear: function()
+    {
+        this._cookiesTable.clear();
+        this._update();
+    },
+
+    _clearButtonClicked: function()
+    {
+        this.clear();
+    },
+
+    _showDeleteButton: function()
+    {
+        this._deleteButton.visible = true;
+    },
+
+    _deleteButtonClicked: function()
+    {
+        var selectedCookie = this._cookiesTable.selectedCookie();
+        if (selectedCookie) {
+            selectedCookie.remove();
+            this._update();
+        }
+    },
+
+    _refreshButtonClicked: function(event)
+    {
+        this._update();
+    },
+
+    _contextMenu: function(event)
+    {
+        if (!this._cookies.length) {
+            var contextMenu = new WebInspector.ContextMenu(event);
+            contextMenu.appendItem(WebInspector.UIString("Refresh"), this._update.bind(this));
+            contextMenu.show();
+        }
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/CookieParser.js b/Source/devtools/front_end/CookieParser.js
new file mode 100644
index 0000000..727a637
--- /dev/null
+++ b/Source/devtools/front_end/CookieParser.js
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+// Ideally, we would rely on platform support for parsing a cookie, since
+// this would save us from any potential inconsistency. However, exposing
+// platform cookie parsing logic would require quite a bit of additional
+// plumbing, and at least some platforms lack support for parsing Cookie,
+// which is in a format slightly different from Set-Cookie and is normally
+// only required on the server side.
+
+/**
+ * @constructor
+ */
+WebInspector.CookieParser = function()
+{
+}
+
+/**
+ * @constructor
+ * @param {string} key
+ * @param {string|undefined} value
+ * @param {number} position
+ */
+WebInspector.CookieParser.KeyValue = function(key, value, position)
+{
+    this.key = key;
+    this.value = value;
+    this.position = position;
+}
+
+WebInspector.CookieParser.prototype = {
+    /**
+     * @return {Array.<WebInspector.Cookie>}
+     */
+    cookies: function()
+    {
+        return this._cookies;
+    },
+
+    /**
+     * @param {string|undefined} cookieHeader
+     * @return {?Array.<WebInspector.Cookie>}
+     */
+    parseCookie: function(cookieHeader)
+    {
+        if (!this._initialize(cookieHeader))
+            return null;
+
+        for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) {
+            if (kv.key.charAt(0) === "$" && this._lastCookie)
+                this._lastCookie.addAttribute(kv.key.slice(1), kv.value);
+            else if (kv.key.toLowerCase() !== "$version" && typeof kv.value === "string")
+                this._addCookie(kv, WebInspector.Cookie.Type.Request);
+            this._advanceAndCheckCookieDelimiter();
+        }
+        this._flushCookie();
+        return this._cookies;
+    },
+
+    /**
+     * @param {string|undefined} setCookieHeader
+     * @return {?Array.<WebInspector.Cookie>}
+     */
+    parseSetCookie: function(setCookieHeader)
+    {
+        if (!this._initialize(setCookieHeader))
+            return null;
+        for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) {
+            if (this._lastCookie)
+                this._lastCookie.addAttribute(kv.key, kv.value);
+            else
+                this._addCookie(kv, WebInspector.Cookie.Type.Response);
+            if (this._advanceAndCheckCookieDelimiter())
+                this._flushCookie();
+        }
+        this._flushCookie();
+        return this._cookies;
+    },
+
+    /**
+     * @param {string|undefined} headerValue
+     * @return {boolean}
+     */
+    _initialize: function(headerValue)
+    {
+        this._input = headerValue;
+        if (typeof headerValue !== "string")
+            return false;
+        this._cookies = [];
+        this._lastCookie = null;
+        this._originalInputLength = this._input.length;
+        return true;
+    },
+
+    _flushCookie: function()
+    {
+        if (this._lastCookie)
+            this._lastCookie.setSize(this._originalInputLength - this._input.length - this._lastCookiePosition);
+        this._lastCookie = null;
+    },
+
+    /**
+     * @return {WebInspector.CookieParser.KeyValue}
+     */
+    _extractKeyValue: function()
+    {
+        if (!this._input || !this._input.length)
+            return null;
+        // Note: RFCs offer an option for quoted values that may contain commas and semicolons.
+        // Many browsers/platforms do not support this, however (see http://webkit.org/b/16699
+        // and http://crbug.com/12361). The logic below matches latest versions of IE, Firefox,
+        // Chrome and Safari on some old platforms. The latest version of Safari supports quoted
+        // cookie values, though.
+        var keyValueMatch = /^[ \t]*([^\s=;]+)[ \t]*(?:=[ \t]*([^;\n]*))?/.exec(this._input);
+        if (!keyValueMatch) {
+            console.log("Failed parsing cookie header before: " + this._input);
+            return null;
+        }
+
+        var result = new WebInspector.CookieParser.KeyValue(keyValueMatch[1], keyValueMatch[2] && keyValueMatch[2].trim(), this._originalInputLength - this._input.length);
+        this._input = this._input.slice(keyValueMatch[0].length);
+        return result;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _advanceAndCheckCookieDelimiter: function()
+    {
+        var match = /^\s*[\n;]\s*/.exec(this._input);
+        if (!match)
+            return false;
+        this._input = this._input.slice(match[0].length);
+        return match[0].match("\n") !== null;
+    },
+
+    /**
+     * @param {!WebInspector.CookieParser.KeyValue} keyValue
+     * @param {!WebInspector.Cookie.Type} type
+     */
+    _addCookie: function(keyValue, type)
+    {
+        if (this._lastCookie)
+            this._lastCookie.setSize(keyValue.position - this._lastCookiePosition);
+        // Mozilla bug 169091: Mozilla, IE and Chrome treat single token (w/o "=") as
+        // specifying a value for a cookie with empty name.
+        this._lastCookie = typeof keyValue.value === "string" ? new WebInspector.Cookie(keyValue.key, keyValue.value, type) :
+            new WebInspector.Cookie("", keyValue.key, type);
+        this._lastCookiePosition = keyValue.position;
+        this._cookies.push(this._lastCookie);
+    }
+};
+
+/**
+ * @param {string|undefined} header
+ * @return {?Array.<WebInspector.Cookie>}
+ */
+WebInspector.CookieParser.parseCookie = function(header)
+{
+    return (new WebInspector.CookieParser()).parseCookie(header);
+}
+
+/**
+ * @param {string|undefined} header
+ * @return {?Array.<WebInspector.Cookie>}
+ */
+WebInspector.CookieParser.parseSetCookie = function(header)
+{
+    return (new WebInspector.CookieParser()).parseSetCookie(header);
+}
+
+/**
+ * @constructor
+ * @param {string} name
+ * @param {string} value
+ * @param {?WebInspector.Cookie.Type} type
+ */
+WebInspector.Cookie = function(name, value, type)
+{
+    this._name = name;
+    this._value = value;
+    this._type = type;
+    this._attributes = {};
+}
+
+WebInspector.Cookie.prototype = {
+    /**
+     * @return {string}
+     */
+    name: function()
+    {
+        return this._name;
+    },
+
+    /**
+     * @return {string}
+     */
+    value: function()
+    {
+        return this._value;
+    },
+
+    /**
+     * @return {?WebInspector.Cookie.Type}
+     */
+    type: function()
+    {
+        return this._type;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    httpOnly: function()
+    {
+        return "httponly" in this._attributes;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    secure: function()
+    {
+        return "secure" in this._attributes;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    session: function()
+    {
+        // RFC 2965 suggests using Discard attribute to mark session cookies, but this does not seem to be widely used.
+        // Check for absence of explicitly max-age or expiry date instead.
+        return !("expires" in this._attributes || "max-age" in this._attributes);
+    },
+
+    /**
+     * @return {string}
+     */
+    path: function()
+    {
+        return this._attributes["path"];
+    },
+
+    /**
+     * @return {string}
+     */
+    port: function()
+    {
+        return this._attributes["port"];
+    },
+
+    /**
+     * @return {string}
+     */
+    domain: function()
+    {
+        return this._attributes["domain"];
+    },
+
+    /**
+     * @return {string}
+     */
+    expires: function()
+    {
+        return this._attributes["expires"];
+    },
+
+    /**
+     * @return {string}
+     */
+    maxAge: function()
+    {
+        return this._attributes["max-age"];
+    },
+
+    /**
+     * @return {number}
+     */
+    size: function()
+    {
+        return this._size;
+    },
+
+    /**
+     * @param {number} size
+     */
+    setSize: function(size)
+    {
+        this._size = size;
+    },
+
+    /**
+     * @return {Date}
+     */
+    expiresDate: function(requestDate)
+    {
+        // RFC 6265 indicates that the max-age attribute takes precedence over the expires attribute
+        if (this.maxAge()) {
+            var targetDate = requestDate === null ? new Date() : requestDate;
+            return new Date(targetDate.getTime() + 1000 * this.maxAge());
+        }
+
+        if (this.expires())
+            return new Date(this.expires());
+
+        return null;
+    },
+
+    /**
+     * @return {Object}
+     */
+    attributes: function()
+    {
+        return this._attributes;
+    },
+
+    /**
+     * @param {string} key 
+     * @param {string=} value 
+     */
+    addAttribute: function(key, value)
+    {
+        this._attributes[key.toLowerCase()] = value;
+    },
+
+    /**
+     * @param {function(?Protocol.Error)=} callback
+     */
+    remove: function(callback)
+    {
+        PageAgent.deleteCookie(this.name(), (this.secure() ? "https://" : "http://") + this.domain() + this.path(), callback);
+    }
+}
+
+/**
+ * @enum {number}
+ */
+WebInspector.Cookie.Type = {
+    Request: 0,
+    Response: 1
+};
+
+WebInspector.Cookies = {}
+
+/**
+ * @param {function(!Array.<!WebInspector.Cookie>)} callback
+ */
+WebInspector.Cookies.getCookiesAsync = function(callback)
+{
+    /**
+     * @param {?Protocol.Error} error 
+     * @param {Array.<PageAgent.Cookie>} cookies
+     * @param {string} cookiesString
+     */
+    function mycallback(error, cookies, cookiesString)
+    {
+        if (error)
+            return;
+        callback(cookies.map(WebInspector.Cookies.buildCookieProtocolObject));
+    }
+
+    PageAgent.getCookies(mycallback);
+}
+
+/**
+ * @param {!PageAgent.Cookie} protocolCookie
+ * @return {!WebInspector.Cookie}
+ */
+WebInspector.Cookies.buildCookieProtocolObject = function(protocolCookie)
+{
+    var cookie = new WebInspector.Cookie(protocolCookie.name, protocolCookie.value, null);
+    cookie.addAttribute("domain", protocolCookie["domain"]);
+    cookie.addAttribute("path", protocolCookie["path"]);
+    cookie.addAttribute("port", protocolCookie["port"]);
+    if (protocolCookie["expires"])
+        cookie.addAttribute("expires", protocolCookie["expires"]);
+    if (protocolCookie["httpOnly"])
+        cookie.addAttribute("httpOnly");
+    if (protocolCookie["secure"])
+        cookie.addAttribute("secure");
+    cookie.setSize(protocolCookie["size"]);
+    return cookie;
+}
+
+/**
+ * @param {WebInspector.Cookie} cookie 
+ * @param {string} resourceURL
+ */
+WebInspector.Cookies.cookieMatchesResourceURL = function(cookie, resourceURL)
+{
+    var url = resourceURL.asParsedURL();
+    if (!url || !WebInspector.Cookies.cookieDomainMatchesResourceDomain(cookie.domain(), url.host))
+        return false;
+    return (url.path.startsWith(cookie.path())
+        && (!cookie.port() || url.port == cookie.port())
+        && (!cookie.secure() || url.scheme === "https"));
+}
+
+/**
+ * @param {string} cookieDomain 
+ * @param {string} resourceDomain
+ */
+WebInspector.Cookies.cookieDomainMatchesResourceDomain = function(cookieDomain, resourceDomain)
+{
+    if (cookieDomain.charAt(0) !== '.')
+        return resourceDomain === cookieDomain;
+    return !!resourceDomain.match(new RegExp("^([^\\.]+\\.)*" + cookieDomain.substring(1).escapeForRegExp() + "$", "i"));
+}
diff --git a/Source/devtools/front_end/CookiesTable.js b/Source/devtools/front_end/CookiesTable.js
new file mode 100644
index 0000000..0af7e2f
--- /dev/null
+++ b/Source/devtools/front_end/CookiesTable.js
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2009 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Joseph Pecoraro
+ * Copyright (C) 2010 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {boolean} expandable
+ * @param {function()=} refreshCallback
+ * @param {function()=} selectedCallback
+ */
+WebInspector.CookiesTable = function(expandable, refreshCallback, selectedCallback)
+{
+    WebInspector.View.call(this);
+    this.element.className = "fill";
+
+    var readOnly = expandable;
+    this._refreshCallback = refreshCallback;
+
+    var columns = [
+        {id: "name", title: WebInspector.UIString("Name"), sortable: true, disclosure: expandable, sort: WebInspector.DataGrid.Order.Ascending, longText: true, weight: 24},
+        {id: "value", title: WebInspector.UIString("Value"), sortable: true, longText: true, weight: 34},
+        {id: "domain", title: WebInspector.UIString("Domain"), sortable: true, weight: 7},
+        {id: "path", title: WebInspector.UIString("Path"), sortable: true, weight: 7},
+        {id: "expires", title: WebInspector.UIString("Expires / Max-Age"), sortable: true, weight: 7},
+        {id: "size", title: WebInspector.UIString("Size"), sortable: true, align: WebInspector.DataGrid.Align.Right, weight: 7},
+        {id: "httpOnly", title: WebInspector.UIString("HTTP"), sortable: true, align: WebInspector.DataGrid.Align.Center, weight: 7},
+        {id: "secure", title: WebInspector.UIString("Secure"), sortable: true, align: WebInspector.DataGrid.Align.Center, weight: 7}
+    ];
+
+    if (readOnly)
+        this._dataGrid = new WebInspector.DataGrid(columns);
+    else
+        this._dataGrid = new WebInspector.DataGrid(columns, undefined, this._onDeleteCookie.bind(this), refreshCallback, this._onContextMenu.bind(this));
+
+    this._dataGrid.setName("cookiesTable");
+    this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._rebuildTable, this);
+
+    if (selectedCallback)
+        this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, selectedCallback, this);
+
+    this._nextSelectedCookie = /** @type {?WebInspector.Cookie} */ (null);
+
+    this._dataGrid.show(this.element);
+    this._data = [];
+}
+
+WebInspector.CookiesTable.prototype = {
+    /**
+     * @param {?string} domain
+     */
+    _clearAndRefresh: function(domain)
+    {
+        this.clear(domain);
+        this._refresh();
+    },
+
+    /**
+     * @param {!WebInspector.ContextMenu} contextMenu
+     * @param {WebInspector.DataGridNode} node
+     */
+    _onContextMenu: function(contextMenu, node)
+    {
+        if (node === this._dataGrid.creationNode)
+            return;
+        var cookie = node.cookie;
+        var domain = cookie.domain();
+        if (domain)
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear all from \"%s\"" : "Clear All from \"%s\"", domain), this._clearAndRefresh.bind(this, domain));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear all" : "Clear All"), this._clearAndRefresh.bind(this, null));
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.Cookie>} cookies
+     */
+    setCookies: function(cookies)
+    {
+        this.setCookieFolders([{cookies: cookies}]);
+    },
+
+    /**
+     * @param {!Array.<!{folderName: ?string, cookies: !Array.<!WebInspector.Cookie>}>} cookieFolders
+     */
+    setCookieFolders: function(cookieFolders)
+    {
+        this._data = cookieFolders;
+        this._rebuildTable();
+    },
+
+    /**
+     * @return {?WebInspector.Cookie}
+     */
+    selectedCookie: function()
+    {
+        var node = this._dataGrid.selectedNode;
+        return node ? node.cookie : null;
+    },
+
+    /**
+     * @param {?string=} domain
+     */
+    clear: function(domain)
+    {
+        for (var i = 0, length = this._data.length; i < length; ++i) {
+            var cookies = this._data[i].cookies;
+            for (var j = 0, cookieCount = cookies.length; j < cookieCount; ++j) {
+                if (!domain || cookies[j].domain() === domain)
+                    cookies[j].remove();
+            }
+        }
+    },
+
+    _rebuildTable: function()
+    {
+        var selectedCookie = this._nextSelectedCookie || this.selectedCookie();
+        this._nextSelectedCookie = null;
+        this._dataGrid.rootNode().removeChildren();
+        for (var i = 0; i < this._data.length; ++i) {
+            var item = this._data[i];
+            if (item.folderName) {
+                var groupData = {name: item.folderName, value: "", domain: "", path: "", expires: "", size: this._totalSize(item.cookies), httpOnly: "", secure: ""};
+                var groupNode = new WebInspector.DataGridNode(groupData);
+                groupNode.selectable = true;
+                this._dataGrid.rootNode().appendChild(groupNode);
+                groupNode.element.addStyleClass("row-group");
+                this._populateNode(groupNode, item.cookies, selectedCookie);
+                groupNode.expand();
+            } else
+                this._populateNode(this._dataGrid.rootNode(), item.cookies, selectedCookie);
+        }
+    },
+
+    /**
+     * @param {!WebInspector.DataGridNode} parentNode
+     * @param {?Array.<!WebInspector.Cookie>} cookies
+     * @param {?WebInspector.Cookie} selectedCookie
+     */
+    _populateNode: function(parentNode, cookies, selectedCookie)
+    {
+        parentNode.removeChildren();
+        if (!cookies)
+            return;
+
+        this._sortCookies(cookies);
+        for (var i = 0; i < cookies.length; ++i) {
+            var cookie = cookies[i];
+            var cookieNode = this._createGridNode(cookie);
+            parentNode.appendChild(cookieNode);
+            if (selectedCookie && selectedCookie.name() === cookie.name() && selectedCookie.domain() === cookie.domain() && selectedCookie.path() === cookie.path())
+                cookieNode.select();
+        }
+    },
+
+    _totalSize: function(cookies)
+    {
+        var totalSize = 0;
+        for (var i = 0; cookies && i < cookies.length; ++i)
+            totalSize += cookies[i].size();
+        return totalSize;
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.Cookie>} cookies
+     */
+    _sortCookies: function(cookies)
+    {
+        var sortDirection = this._dataGrid.isSortOrderAscending() ? 1 : -1;
+
+        function compareTo(getter, cookie1, cookie2)
+        {
+            return sortDirection * (getter.apply(cookie1) + "").compareTo(getter.apply(cookie2) + "")
+        }
+
+        function numberCompare(getter, cookie1, cookie2)
+        {
+            return sortDirection * (getter.apply(cookie1) - getter.apply(cookie2));
+        }
+
+        function expiresCompare(cookie1, cookie2)
+        {
+            if (cookie1.session() !== cookie2.session())
+                return sortDirection * (cookie1.session() ? 1 : -1);
+
+            if (cookie1.session())
+                return 0;
+
+            if (cookie1.maxAge() && cookie2.maxAge())
+                return sortDirection * (cookie1.maxAge() - cookie2.maxAge());
+            if (cookie1.expires() && cookie2.expires())
+                return sortDirection * (cookie1.expires() - cookie2.expires());
+            return sortDirection * (cookie1.expires() ? 1 : -1);
+        }
+
+        var comparator;
+        switch (this._dataGrid.sortColumnIdentifier()) {
+            case "name": comparator = compareTo.bind(null, WebInspector.Cookie.prototype.name); break;
+            case "value": comparator = compareTo.bind(null, WebInspector.Cookie.prototype.value); break;
+            case "domain": comparator = compareTo.bind(null, WebInspector.Cookie.prototype.domain); break;
+            case "path": comparator = compareTo.bind(null, WebInspector.Cookie.prototype.path); break;
+            case "expires": comparator = expiresCompare; break;
+            case "size": comparator = numberCompare.bind(null, WebInspector.Cookie.prototype.size); break;
+            case "httpOnly": comparator = compareTo.bind(null, WebInspector.Cookie.prototype.httpOnly); break;
+            case "secure": comparator = compareTo.bind(null, WebInspector.Cookie.prototype.secure); break;
+            default: compareTo.bind(null, WebInspector.Cookie.prototype.name);
+        }
+
+        cookies.sort(comparator);
+    },
+
+    /**
+     * @param {!WebInspector.Cookie} cookie
+     * @return {!WebInspector.DataGridNode}
+     */
+    _createGridNode: function(cookie)
+    {
+        var data = {};
+        data.name = cookie.name();
+        data.value = cookie.value();
+        data.domain = cookie.domain() || "";
+        data.path = cookie.path() || "";
+        if (cookie.type() === WebInspector.Cookie.Type.Request)
+            data.expires = "";
+        else if (cookie.maxAge())
+            data.expires = Number.secondsToString(parseInt(cookie.maxAge(), 10));
+        else if (cookie.expires())
+            data.expires = new Date(cookie.expires()).toGMTString();
+        else
+            data.expires = WebInspector.UIString("Session");
+        data.size = cookie.size();
+        const checkmark = "\u2713";
+        data.httpOnly = (cookie.httpOnly() ? checkmark : "");
+        data.secure = (cookie.secure() ? checkmark : "");
+
+        var node = new WebInspector.DataGridNode(data);
+        node.cookie = cookie;
+        node.selectable = true;
+        return node;
+    },
+
+    _onDeleteCookie: function(node)
+    {
+        var cookie = node.cookie;
+        var neighbour = node.traverseNextNode() || node.traversePreviousNode();
+        if (neighbour)
+            this._nextSelectedCookie = neighbour.cookie;
+        cookie.remove();
+        this._refresh();
+    },
+
+    _refresh: function()
+    {
+        if (this._refreshCallback)
+            this._refreshCallback();
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/DOMAgent.js b/Source/devtools/front_end/DOMAgent.js
new file mode 100644
index 0000000..d8dbc34
--- /dev/null
+++ b/Source/devtools/front_end/DOMAgent.js
@@ -0,0 +1,1514 @@
+/*
+ * Copyright (C) 2009, 2010 Google 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.DOMAgent} domAgent
+ * @param {?WebInspector.DOMDocument} doc
+ * @param {boolean} isInShadowTree
+ * @param {DOMAgent.Node} payload
+ */
+WebInspector.DOMNode = function(domAgent, doc, isInShadowTree, payload) {
+    this._domAgent = domAgent;
+    this.ownerDocument = doc;
+    this._isInShadowTree = isInShadowTree;
+
+    this.id = payload.nodeId;
+    domAgent._idToDOMNode[this.id] = this;
+    this._nodeType = payload.nodeType;
+    this._nodeName = payload.nodeName;
+    this._localName = payload.localName;
+    this._nodeValue = payload.nodeValue;
+
+    this._shadowRoots = [];
+
+    this._attributes = [];
+    this._attributesMap = {};
+    if (payload.attributes)
+        this._setAttributesPayload(payload.attributes);
+
+    this._userProperties = {};
+    this._descendantUserPropertyCounters = {};
+
+    this._childNodeCount = payload.childNodeCount;
+    this.children = null;
+
+    this.nextSibling = null;
+    this.previousSibling = null;
+    this.firstChild = null;
+    this.lastChild = null;
+    this.parentNode = null;
+
+    if (payload.shadowRoots && WebInspector.settings.showShadowDOM.get()) {
+        for (var i = 0; i < payload.shadowRoots.length; ++i) {
+            var root = payload.shadowRoots[i];
+            var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, true, root);
+            this._shadowRoots.push(node);
+        }
+    }
+
+    if (payload.templateContent)
+        this._templateContent = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, true, payload.templateContent);
+
+    if (payload.children)
+        this._setChildrenPayload(payload.children);
+
+    if (payload.contentDocument) {
+        this._contentDocument = new WebInspector.DOMDocument(domAgent, payload.contentDocument);
+        this.children = [this._contentDocument];
+        this._renumber();
+    }
+
+    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;
+    } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) {
+        this.publicId = payload.publicId;
+        this.systemId = payload.systemId;
+        this.internalSubset = payload.internalSubset;
+    } else if (this._nodeType === Node.ATTRIBUTE_NODE) {
+        this.name = payload.name;
+        this.value = payload.value;
+    }
+}
+
+/**
+ * @constructor
+ * @param {string} value
+ * @param {boolean} optimized
+ */
+WebInspector.DOMNode.XPathStep = function(value, optimized)
+{
+    this.value = value;
+    this.optimized = optimized;
+}
+
+WebInspector.DOMNode.XPathStep.prototype = {
+    toString: function()
+    {
+        return this.value;
+    }
+}
+
+WebInspector.DOMNode.prototype = {
+    /**
+     * @return {boolean}
+     */
+    hasAttributes: function()
+    {
+        return this._attributes.length > 0;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    hasChildNodes: function()
+    {
+        return this._childNodeCount > 0 || !!this._shadowRoots.length || !!this._templateContent;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    hasShadowRoots: function()
+    {
+        return !!this._shadowRoots.length;
+    },
+
+    /**
+     * @return {number}
+     */
+    nodeType: function()
+    {
+        return this._nodeType;
+    },
+
+    /**
+     * @return {string}
+     */
+    nodeName: function()
+    {
+        return this._nodeName;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isInShadowTree: function()
+    {
+        return this._isInShadowTree;
+    },
+
+    /**
+     * @return {string}
+     */
+    nodeNameInCorrectCase: function()
+    {
+        return this.isXMLNode() ? this.nodeName() : this.nodeName().toLowerCase();
+    },
+
+    /**
+     * @param {string} name
+     * @param {function(?Protocol.Error)=} callback
+     */
+    setNodeName: function(name, callback)
+    {
+        DOMAgent.setNodeName(this.id, name, WebInspector.domAgent._markRevision(this, callback));
+    },
+
+    /**
+     * @return {string}
+     */
+    localName: function()
+    {
+        return this._localName;
+    },
+
+    /**
+     * @return {string}
+     */
+    nodeValue: function()
+    {
+        return this._nodeValue;
+    },
+
+    /**
+     * @param {string} value
+     * @param {function(?Protocol.Error)=} callback
+     */
+    setNodeValue: function(value, callback)
+    {
+        DOMAgent.setNodeValue(this.id, value, WebInspector.domAgent._markRevision(this, callback));
+    },
+
+    /**
+     * @param {string} name
+     * @return {string}
+     */
+    getAttribute: function(name)
+    {
+        var attr = this._attributesMap[name];
+        return attr ? attr.value : undefined;
+    },
+
+    /**
+     * @param {string} name
+     * @param {string} text
+     * @param {function(?Protocol.Error)=} callback
+     */
+    setAttribute: function(name, text, callback)
+    {
+        DOMAgent.setAttributesAsText(this.id, text, name, WebInspector.domAgent._markRevision(this, callback));
+    },
+
+    /**
+     * @param {string} name
+     * @param {string} value
+     * @param {function(?Protocol.Error)=} callback
+     */
+    setAttributeValue: function(name, value, callback)
+    {
+        DOMAgent.setAttributeValue(this.id, name, value, WebInspector.domAgent._markRevision(this, callback));
+    },
+
+    /**
+     * @return {Object}
+     */
+    attributes: function()
+    {
+        return this._attributes;
+    },
+
+    /**
+     * @param {string} name
+     * @param {function(?Protocol.Error)=} callback
+     */
+    removeAttribute: function(name, callback)
+    {
+        /**
+         *  @param {?Protocol.Error} error
+         */
+        function mycallback(error)
+        {
+            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;
+                    }
+                }
+            }
+
+            WebInspector.domAgent._markRevision(this, callback)(error);
+        }
+        DOMAgent.removeAttribute(this.id, name, mycallback.bind(this));
+    },
+
+    /**
+     * @param {function(Array.<WebInspector.DOMNode>)=} callback
+     */
+    getChildNodes: function(callback)
+    {
+        if (this.children) {
+            if (callback)
+                callback(this.children);
+            return;
+        }
+
+        /**
+         * @this {WebInspector.DOMNode}
+         * @param {?Protocol.Error} error
+         */
+        function mycallback(error)
+        {
+            if (!error && callback)
+                callback(this.children);
+        }
+
+        DOMAgent.requestChildNodes(this.id, undefined, mycallback.bind(this));
+    },
+
+    /**
+     * @param {number} depth
+     * @param {function(Array.<WebInspector.DOMNode>)=} callback
+     */
+    getSubtree: function(depth, callback)
+    {
+        /**
+         * @this {WebInspector.DOMNode}
+         * @param {?Protocol.Error} error
+         */
+        function mycallback(error)
+        {
+            if (callback)
+                callback(error ? null : this.children);                
+        }
+
+        DOMAgent.requestChildNodes(this.id, depth, mycallback.bind(this));
+    },
+
+    /**
+     * @param {function(?Protocol.Error)=} callback
+     */
+    getOuterHTML: function(callback)
+    {
+        DOMAgent.getOuterHTML(this.id, callback);
+    },
+
+    /**
+     * @param {string} html
+     * @param {function(?Protocol.Error)=} callback
+     */
+    setOuterHTML: function(html, callback)
+    {
+        DOMAgent.setOuterHTML(this.id, html, WebInspector.domAgent._markRevision(this, callback));
+    },
+
+    /**
+     * @param {function(?Protocol.Error)=} callback
+     */
+    removeNode: function(callback)
+    {
+        DOMAgent.removeNode(this.id, WebInspector.domAgent._markRevision(this, callback));
+    },
+
+    copyNode: function()
+    {
+        function copy(error, text)
+        {
+            if (!error)
+                InspectorFrontendHost.copyText(text);
+        }
+        DOMAgent.getOuterHTML(this.id, copy);
+    },
+
+    /**
+     * @param {boolean} optimized
+     */
+    copyXPath: function(optimized)
+    {
+        InspectorFrontendHost.copyText(this.xPath(optimized));
+    },
+
+    /**
+     * @param {string} objectGroupId
+     * @param {function(?Protocol.Error)=} callback
+     */
+    eventListeners: function(objectGroupId, callback)
+    {
+        DOMAgent.getEventListenersForNode(this.id, objectGroupId, callback);
+    },
+
+    /**
+     * @return {string}
+     */
+    path: function()
+    {
+        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(",");
+    },
+
+    /**
+     * @param {boolean} justSelector
+     * @return {string}
+     */
+    appropriateSelectorFor: function(justSelector)
+    {
+        var lowerCaseName = this.localName() || this.nodeName().toLowerCase();
+
+        var id = this.getAttribute("id");
+        if (id) {
+            var selector = "#" + id;
+            return (justSelector ? selector : lowerCaseName + selector);
+        }
+
+        var className = this.getAttribute("class");
+        if (className) {
+            var selector = "." + className.trim().replace(/\s+/g, ".");
+            return (justSelector ? selector : lowerCaseName + selector);
+        }
+
+        if (lowerCaseName === "input" && this.getAttribute("type"))
+            return lowerCaseName + "[type=\"" + this.getAttribute("type") + "\"]";
+
+        return lowerCaseName;
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     * @return {boolean}
+     */
+    isAncestor: function(node)
+    {
+        if (!node)
+            return false;
+
+        var currentNode = node.parentNode;
+        while (currentNode) {
+            if (this === currentNode)
+                return true;
+            currentNode = currentNode.parentNode;
+        }
+        return false;
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} descendant
+     * @return {boolean}
+     */
+    isDescendant: function(descendant)
+    {
+        return descendant !== null && descendant.isAncestor(this);
+    },
+
+    /**
+     * @param {Array.<string>} attrs
+     * @return {boolean}
+     */
+    _setAttributesPayload: function(attrs)
+    {
+        var attributesChanged = !this._attributes || attrs.length !== this._attributes.length * 2;
+        var oldAttributesMap = this._attributesMap || {};
+
+        this._attributes = [];
+        this._attributesMap = {};
+
+        for (var i = 0; i < attrs.length; i += 2) {
+            var name = attrs[i];
+            var value = attrs[i + 1];
+            this._addAttribute(name, value);
+
+            if (attributesChanged)
+                continue;
+
+            if (!oldAttributesMap[name] || oldAttributesMap[name].value !== value)
+              attributesChanged = true;
+        }
+        return attributesChanged;
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} prev
+     * @param {DOMAgent.Node} payload
+     * @return {WebInspector.DOMNode}
+     */
+    _insertChild: function(prev, payload)
+    {
+        var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payload);
+        if (!prev) {
+            if (!this.children) {
+                // First node
+                this.children = this._shadowRoots.slice();
+                if (this._templateContent)
+                    this.children.push(this._templateContent);
+
+                this.children.push(node);
+            } else
+                this.children.unshift(node);
+        } else
+            this.children.splice(this.children.indexOf(prev) + 1, 0, node);
+        this._renumber();
+        return node;
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     */
+    _removeChild: function(node)
+    {
+        this.children.splice(this.children.indexOf(node), 1);
+        node.parentNode = null;
+        node._updateChildUserPropertyCountsOnRemoval(this);
+        this._renumber();
+    },
+
+    /**
+     * @param {Array.<DOMAgent.Node>} payloads
+     */
+    _setChildrenPayload: function(payloads)
+    {
+        // We set children in the constructor.
+        if (this._contentDocument)
+            return;
+
+        this.children = this._shadowRoots.slice();
+        if (this._templateContent)
+            this.children.push(this._templateContent);
+
+        for (var i = 0; i < payloads.length; ++i) {
+            var payload = payloads[i];
+            var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payload);
+            this.children.push(node);
+        }
+        this._renumber();
+    },
+
+    _renumber: function()
+    {
+        this._childNodeCount = this.children.length;
+        if (this._childNodeCount == 0) {
+            this.firstChild = null;
+            this.lastChild = null;
+            return;
+        }
+        this.firstChild = this.children[0];
+        this.lastChild = this.children[this._childNodeCount - 1];
+        for (var i = 0; i < this._childNodeCount; ++i) {
+            var child = this.children[i];
+            child.index = i;
+            child.nextSibling = i + 1 < this._childNodeCount ? this.children[i + 1] : null;
+            child.previousSibling = i - 1 >= 0 ? this.children[i - 1] : null;
+            child.parentNode = this;
+        }
+    },
+
+    /**
+     * @param {string} name
+     * @param {string} value
+     */
+    _addAttribute: function(name, value)
+    {
+        var attr = {
+            name: name,
+            value: value,
+            _node: this
+        };
+        this._attributesMap[name] = attr;
+        this._attributes.push(attr);
+    },
+
+    /**
+     * @param {string} name
+     * @param {string} value
+     */
+    _setAttribute: function(name, value)
+    {
+        var attr = this._attributesMap[name];
+        if (attr)
+            attr.value = value;
+        else
+            this._addAttribute(name, value);
+    },
+
+    /**
+     * @param {string} name
+     */
+    _removeAttribute: function(name)
+    {
+        var attr = this._attributesMap[name];
+        if (attr) {
+            this._attributes.remove(attr);
+            delete this._attributesMap[name];
+        }
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} targetNode
+     * @param {?WebInspector.DOMNode} anchorNode
+     * @param {function(?Protocol.Error)=} callback
+     */
+    moveTo: function(targetNode, anchorNode, callback)
+    {
+        DOMAgent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, WebInspector.domAgent._markRevision(this, callback));
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isXMLNode: function()
+    {
+        return !!this.ownerDocument && !!this.ownerDocument.xmlVersion;
+    },
+
+    /**
+     * @param {boolean} optimized
+     * @return {string}
+     */
+    xPath: function(optimized)
+    {
+        if (this._nodeType === Node.DOCUMENT_NODE)
+            return "/";
+
+        var steps = [];
+        var contextNode = this;
+        while (contextNode) {
+            var step = contextNode._xPathValue(optimized);
+            if (!step)
+                break; // Error - bail out early.
+            steps.push(step);
+            if (step.optimized)
+                break;
+            contextNode = contextNode.parentNode;
+        }
+
+        steps.reverse();
+        return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/");
+    },
+
+    /**
+     * @param {boolean} optimized
+     * @return {WebInspector.DOMNode.XPathStep}
+     */
+    _xPathValue: function(optimized)
+    {
+        var ownValue;
+        var ownIndex = this._xPathIndex();
+        if (ownIndex === -1)
+            return null; // Error.
+
+        switch (this._nodeType) {
+        case Node.ELEMENT_NODE:
+            if (optimized && this.getAttribute("id"))
+                return new WebInspector.DOMNode.XPathStep("//*[@id=\"" + this.getAttribute("id") + "\"]", true);
+            ownValue = this._localName;
+            break;
+        case Node.ATTRIBUTE_NODE:
+            ownValue = "@" + this._nodeName;
+            break;
+        case Node.TEXT_NODE:
+        case Node.CDATA_SECTION_NODE:
+            ownValue = "text()";
+            break;
+        case Node.PROCESSING_INSTRUCTION_NODE:
+            ownValue = "processing-instruction()";
+            break;
+        case Node.COMMENT_NODE:
+            ownValue = "comment()";
+            break;
+        case Node.DOCUMENT_NODE:
+            ownValue = "";
+            break;
+        default:
+            ownValue = "";
+            break;
+        }
+
+        if (ownIndex > 0)
+            ownValue += "[" + ownIndex + "]";
+
+        return new WebInspector.DOMNode.XPathStep(ownValue, this._nodeType === Node.DOCUMENT_NODE);
+    },
+
+    /**
+     * @return {number}
+     */
+    _xPathIndex: function()
+    {
+        // Returns -1 in case of error, 0 if no siblings matching the same expression, <XPath index among the same expression-matching sibling nodes> otherwise.
+        function areNodesSimilar(left, right)
+        {
+            if (left === right)
+                return true;
+
+            if (left._nodeType === Node.ELEMENT_NODE && right._nodeType === Node.ELEMENT_NODE)
+                return left._localName === right._localName;
+
+            if (left._nodeType === right._nodeType)
+                return true;
+
+            // XPath treats CDATA as text nodes.
+            var leftType = left._nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : left._nodeType;
+            var rightType = right._nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : right._nodeType;
+            return leftType === rightType;
+        }
+
+        var siblings = this.parentNode ? this.parentNode.children : null;
+        if (!siblings)
+            return 0; // Root node - no siblings.
+        var hasSameNamedElements;
+        for (var i = 0; i < siblings.length; ++i) {
+            if (areNodesSimilar(this, siblings[i]) && siblings[i] !== this) {
+                hasSameNamedElements = true;
+                break;
+            }
+        }
+        if (!hasSameNamedElements)
+            return 0;
+        var ownIndex = 1; // XPath indices start with 1.
+        for (var i = 0; i < siblings.length; ++i) {
+            if (areNodesSimilar(this, siblings[i])) {
+                if (siblings[i] === this)
+                    return ownIndex;
+                ++ownIndex;
+            }
+        }
+        return -1; // An error occurred: |this| not found in parent's children.
+    },
+
+    _updateChildUserPropertyCountsOnRemoval: function(parentNode)
+    {
+        var result = {};
+        if (this._userProperties) {
+            for (var name in this._userProperties)
+                result[name] = (result[name] || 0) + 1;
+        }
+
+        if (this._descendantUserPropertyCounters) {
+            for (var name in this._descendantUserPropertyCounters) {
+                var counter = this._descendantUserPropertyCounters[name];
+                result[name] = (result[name] || 0) + counter;
+            }
+        }
+
+        for (var name in result)
+            parentNode._updateDescendantUserPropertyCount(name, -result[name]);
+    },
+
+    _updateDescendantUserPropertyCount: function(name, delta)
+    {
+        if (!this._descendantUserPropertyCounters.hasOwnProperty(name))
+            this._descendantUserPropertyCounters[name] = 0;
+        this._descendantUserPropertyCounters[name] += delta;
+        if (!this._descendantUserPropertyCounters[name])
+            delete this._descendantUserPropertyCounters[name];
+        if (this.parentNode)
+            this.parentNode._updateDescendantUserPropertyCount(name, delta);
+    },
+
+    setUserProperty: function(name, value)
+    {
+        if (value === null) {
+            this.removeUserProperty(name);
+            return;
+        }
+
+        if (this.parentNode && !this._userProperties.hasOwnProperty(name))
+            this.parentNode._updateDescendantUserPropertyCount(name, 1);
+
+        this._userProperties[name] = value;
+    },
+
+    removeUserProperty: function(name)
+    {
+        if (!this._userProperties.hasOwnProperty(name))
+            return;
+
+        delete this._userProperties[name];
+        if (this.parentNode)
+            this.parentNode._updateDescendantUserPropertyCount(name, -1);
+    },
+
+    getUserProperty: function(name)
+    {
+        return this._userProperties ? this._userProperties[name] : null;
+    },
+
+    descendantUserPropertyCount: function(name)
+    {
+        return this._descendantUserPropertyCounters && this._descendantUserPropertyCounters[name] ? this._descendantUserPropertyCounters[name] : 0;
+    },
+
+    /**
+     * @param {string} url
+     * @return {?string}
+     */
+    resolveURL: function(url)
+    {
+        if (!url)
+            return url;
+        for (var frameOwnerCandidate = this; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
+            if (frameOwnerCandidate.baseURL)
+                return WebInspector.ParsedURL.completeURL(frameOwnerCandidate.baseURL, url);
+        }
+        return null;
+    }
+}
+
+/**
+ * @extends {WebInspector.DOMNode}
+ * @constructor
+ * @param {WebInspector.DOMAgent} domAgent
+ * @param {DOMAgent.Node} payload
+ */
+WebInspector.DOMDocument = function(domAgent, payload)
+{
+    WebInspector.DOMNode.call(this, domAgent, this, false, payload);
+    this.documentURL = payload.documentURL || "";
+    this.baseURL = /** @type {string} */ (payload.baseURL);
+    console.assert(this.baseURL);
+    this.xmlVersion = payload.xmlVersion;
+    this._listeners = {};
+}
+
+WebInspector.DOMDocument.prototype = {
+    __proto__: WebInspector.DOMNode.prototype
+}
+
+/**
+ * @extends {WebInspector.Object}
+ * @constructor
+ */
+WebInspector.DOMAgent = function() {
+    /** @type {Object|undefined} */
+    this._idToDOMNode = {};
+    this._document = null;
+    this._attributeLoadNodeIds = {};
+    InspectorBackend.registerDOMDispatcher(new WebInspector.DOMDispatcher(this));
+    if (WebInspector.settings.emulateTouchEvents.get())
+        this._emulateTouchEventsChanged();
+    WebInspector.settings.emulateTouchEvents.addChangeListener(this._emulateTouchEventsChanged, this);
+}
+
+WebInspector.DOMAgent.Events = {
+    AttrModified: "AttrModified",
+    AttrRemoved: "AttrRemoved",
+    CharacterDataModified: "CharacterDataModified",
+    NodeInserted: "NodeInserted",
+    NodeRemoved: "NodeRemoved",
+    DocumentUpdated: "DocumentUpdated",
+    ChildNodeCountUpdated: "ChildNodeCountUpdated",
+    InspectElementRequested: "InspectElementRequested",
+    UndoRedoRequested: "UndoRedoRequested",
+    UndoRedoCompleted: "UndoRedoCompleted",
+    InspectNodeRequested: "InspectNodeRequested"
+}
+
+WebInspector.DOMAgent.prototype = {
+    /**
+     * @param {function(WebInspector.DOMDocument)=} callback
+     */
+    requestDocument: function(callback)
+    {
+        if (this._document) {
+            if (callback)
+                callback(this._document);
+            return;
+        }
+
+        if (this._pendingDocumentRequestCallbacks) {
+            this._pendingDocumentRequestCallbacks.push(callback);
+            return;
+        }
+
+        this._pendingDocumentRequestCallbacks = [callback];
+
+        /**
+         * @this {WebInspector.DOMAgent}
+         * @param {?Protocol.Error} error
+         * @param {DOMAgent.Node} root
+         */
+        function onDocumentAvailable(error, root)
+        {
+            if (!error)
+                this._setDocument(root);
+
+            for (var i = 0; i < this._pendingDocumentRequestCallbacks.length; ++i) {
+                var callback = this._pendingDocumentRequestCallbacks[i];
+                if (callback)
+                    callback(this._document);
+            }
+            delete this._pendingDocumentRequestCallbacks;
+        }
+
+        DOMAgent.getDocument(onDocumentAvailable.bind(this));
+    },
+
+    /**
+     * @return {WebInspector.DOMDocument?}
+     */
+    existingDocument: function()
+    {
+        return this._document;
+    },
+
+    /**
+     * @param {RuntimeAgent.RemoteObjectId} objectId
+     * @param {function(?DOMAgent.NodeId)=} callback
+     */
+    pushNodeToFrontend: function(objectId, callback)
+    {
+        var callbackCast = /** @type {function(*)} */(callback);
+        this._dispatchWhenDocumentAvailable(DOMAgent.requestNode.bind(DOMAgent, objectId), callbackCast);
+    },
+
+    /**
+     * @param {string} path
+     * @param {function(?WebInspector.DOMNode)=} callback
+     */
+    pushNodeByPathToFrontend: function(path, callback)
+    {
+        var callbackCast = /** @type {function(*)} */(callback);
+        this._dispatchWhenDocumentAvailable(DOMAgent.pushNodeByPathToFrontend.bind(DOMAgent, path), callbackCast);
+    },
+
+    /**
+     * @param {function(*)=} callback
+     * @return {function(?Protocol.Error,*=)|undefined}
+     */
+    _wrapClientCallback: function(callback)
+    {
+        if (!callback)
+            return;
+        /**
+         * @param {?Protocol.Error} error
+         * @param {*=} result
+         */
+        return function(error, result)
+        {
+            // Caller is responsible for handling the actual error.
+            callback(error ? null : result);
+        }
+    },
+
+    /**
+     * @param {function(function(?Protocol.Error, *=))} func
+     * @param {function(*)=} callback
+     */
+    _dispatchWhenDocumentAvailable: function(func, callback)
+    {
+        var callbackWrapper = /** @type {function(?Protocol.Error, *=)} */(this._wrapClientCallback(callback));
+
+        function onDocumentAvailable()
+        {
+            if (this._document)
+                func(callbackWrapper);
+            else {
+                if (callbackWrapper)
+                    callbackWrapper("No document");
+            }
+        }
+        this.requestDocument(onDocumentAvailable.bind(this));
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} name
+     * @param {string} value
+     */
+    _attributeModified: function(nodeId, name, value)
+    {
+        var node = this._idToDOMNode[nodeId];
+        if (!node)
+            return;
+
+        node._setAttribute(name, value);
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrModified, { node: node, name: name });
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} name
+     */
+    _attributeRemoved: function(nodeId, name)
+    {
+        var node = this._idToDOMNode[nodeId];
+        if (!node)
+            return;
+        node._removeAttribute(name);
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrRemoved, { node: node, name: name });
+    },
+
+    /**
+     * @param {Array.<DOMAgent.NodeId>} nodeIds
+     */
+    _inlineStyleInvalidated: function(nodeIds)
+    {
+        for (var i = 0; i < nodeIds.length; ++i)
+            this._attributeLoadNodeIds[nodeIds[i]] = true;
+        if ("_loadNodeAttributesTimeout" in this)
+            return;
+        this._loadNodeAttributesTimeout = setTimeout(this._loadNodeAttributes.bind(this), 0);
+    },
+
+    _loadNodeAttributes: function()
+    {
+        /**
+         * @this {WebInspector.DOMAgent}
+         * @param {DOMAgent.NodeId} nodeId
+         * @param {?Protocol.Error} error
+         * @param {Array.<string>} attributes
+         */
+        function callback(nodeId, error, attributes)
+        {
+            if (error) {
+                // We are calling _loadNodeAttributes asynchronously, it is ok if node is not found.
+                return;
+            }
+            var node = this._idToDOMNode[nodeId];
+            if (node) {
+                if (node._setAttributesPayload(attributes))
+                    this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrModified, { node: node, name: "style" });
+            }
+        }
+
+        delete this._loadNodeAttributesTimeout;
+
+        for (var nodeId in this._attributeLoadNodeIds) {
+            var nodeIdAsNumber = parseInt(nodeId, 10);
+            DOMAgent.getAttributes(nodeIdAsNumber, callback.bind(this, nodeIdAsNumber));
+        }
+        this._attributeLoadNodeIds = {};
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} newValue
+     */
+    _characterDataModified: function(nodeId, newValue)
+    {
+        var node = this._idToDOMNode[nodeId];
+        node._nodeValue = newValue;
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.CharacterDataModified, node);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @return {WebInspector.DOMNode|undefined}
+     */
+    nodeForId: function(nodeId)
+    {
+        return this._idToDOMNode[nodeId];
+    },
+
+    _documentUpdated: function()
+    {
+        this._setDocument(null);
+    },
+
+    /**
+     * @param {DOMAgent.Node} payload
+     */
+    _setDocument: function(payload)
+    {
+        this._idToDOMNode = {};
+        if (payload && "nodeId" in payload)
+            this._document = new WebInspector.DOMDocument(this, payload);
+        else
+            this._document = null;
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.DocumentUpdated, this._document);
+    },
+
+    /**
+     * @param {DOMAgent.Node} payload
+     */
+    _setDetachedRoot: function(payload)
+    {
+        if (payload.nodeName === "#document")
+            new WebInspector.DOMDocument(this, payload);
+        else
+            new WebInspector.DOMNode(this, null, false, payload);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} parentId
+     * @param {Array.<DOMAgent.Node>} payloads
+     */
+    _setChildNodes: function(parentId, payloads)
+    {
+        if (!parentId && payloads.length) {
+            this._setDetachedRoot(payloads[0]);
+            return;
+        }
+
+        var parent = this._idToDOMNode[parentId];
+        parent._setChildrenPayload(payloads);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {number} newValue
+     */
+    _childNodeCountUpdated: function(nodeId, newValue)
+    {
+        var node = this._idToDOMNode[nodeId];
+        node._childNodeCount = newValue;
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.ChildNodeCountUpdated, node);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} parentId
+     * @param {DOMAgent.NodeId} prevId
+     * @param {DOMAgent.Node} payload
+     */
+    _childNodeInserted: function(parentId, prevId, payload)
+    {
+        var parent = this._idToDOMNode[parentId];
+        var prev = this._idToDOMNode[prevId];
+        var node = parent._insertChild(prev, payload);
+        this._idToDOMNode[node.id] = node;
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeInserted, node);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} parentId
+     * @param {DOMAgent.NodeId} nodeId
+     */
+    _childNodeRemoved: function(parentId, nodeId)
+    {
+        var parent = this._idToDOMNode[parentId];
+        var node = this._idToDOMNode[nodeId];
+        parent._removeChild(node);
+        this._unbind(node);
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeRemoved, {node: node, parent: parent});
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} rootId
+     */
+    _shadowRootPopped: function(rootId)
+    {
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     */
+    _unbind: function(node)
+    {
+        delete this._idToDOMNode[node.id];
+        for (var i = 0; node.children && i < node.children.length; ++i)
+            this._unbind(node.children[i]);
+    },
+
+    /**
+     * @param {number} nodeId
+     */
+    inspectElement: function(nodeId)
+    {
+        var node = this._idToDOMNode[nodeId];
+        if (node)
+            this.dispatchEventToListeners(WebInspector.DOMAgent.Events.InspectElementRequested, node);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     */
+    _inspectNodeRequested: function(nodeId)
+    {
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.InspectNodeRequested, nodeId);
+    },
+
+    /**
+     * @param {string} query
+     * @param {function(number)} searchCallback
+     */
+    performSearch: function(query, searchCallback)
+    {
+        this.cancelSearch();
+
+        /**
+         * @param {?Protocol.Error} error
+         * @param {string} searchId
+         * @param {number} resultsCount
+         */
+        function callback(error, searchId, resultsCount)
+        {
+            this._searchId = searchId;
+            searchCallback(resultsCount);
+        }
+        DOMAgent.performSearch(query, callback.bind(this));
+    },
+
+    /**
+     * @param {number} index
+     * @param {?function(DOMAgent.Node)} callback
+     */
+    searchResult: function(index, callback)
+    {
+        if (this._searchId) {
+            /**
+             * @param {?Protocol.Error} error
+             * @param {Array.<number>} nodeIds
+             */
+            function mycallback(error, nodeIds)
+            {
+                if (error) {
+                    console.error(error);
+                    callback(null);
+                    return;
+                }
+                if (nodeIds.length != 1)
+                    return;
+
+                callback(this._idToDOMNode[nodeIds[0]]);
+            }
+            DOMAgent.getSearchResults(this._searchId, index, index + 1, mycallback.bind(this));
+        } else
+            callback(null);
+    },
+
+    cancelSearch: function()
+    {
+        if (this._searchId) {
+            DOMAgent.discardSearchResults(this._searchId);
+            delete this._searchId;
+        }
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} selectors
+     * @param {function(?DOMAgent.NodeId)=} callback
+     */
+    querySelector: function(nodeId, selectors, callback)
+    {
+        var callbackCast = /** @type {function(*)|undefined} */(callback);
+        DOMAgent.querySelector(nodeId, selectors, this._wrapClientCallback(callbackCast));
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} selectors
+     * @param {function(?Array.<DOMAgent.NodeId>)=} callback
+     */
+    querySelectorAll: function(nodeId, selectors, callback)
+    {
+        var callbackCast = /** @type {function(*)|undefined} */(callback);
+        DOMAgent.querySelectorAll(nodeId, selectors, this._wrapClientCallback(callbackCast));
+    },
+
+    /**
+     * @param {DOMAgent.NodeId=} nodeId
+     * @param {string=} mode
+     * @param {RuntimeAgent.RemoteObjectId=} objectId
+     */
+    highlightDOMNode: function(nodeId, mode, objectId)
+    {
+        if (this._hideDOMNodeHighlightTimeout) {
+            clearTimeout(this._hideDOMNodeHighlightTimeout);
+            delete this._hideDOMNodeHighlightTimeout;
+        }
+
+        if (objectId || nodeId)
+            DOMAgent.highlightNode(this._buildHighlightConfig(mode), objectId ? undefined : nodeId, objectId);
+        else
+            DOMAgent.hideHighlight();
+    },
+
+    hideDOMNodeHighlight: function()
+    {
+        this.highlightDOMNode(0);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     */
+    highlightDOMNodeForTwoSeconds: function(nodeId)
+    {
+        this.highlightDOMNode(nodeId);
+        this._hideDOMNodeHighlightTimeout = setTimeout(this.hideDOMNodeHighlight.bind(this), 2000);
+    },
+
+    /**
+     * @param {boolean} enabled
+     * @param {function(?Protocol.Error)=} callback
+     */
+    setInspectModeEnabled: function(enabled, callback)
+    {
+        var callbackCast = /** @type {function(*)} */ (callback);
+        this._dispatchWhenDocumentAvailable(DOMAgent.setInspectModeEnabled.bind(DOMAgent, enabled, this._buildHighlightConfig()), callbackCast);
+    },
+
+    /**
+     * @param {string=} mode
+     */
+    _buildHighlightConfig: function(mode)
+    {
+        mode = mode || "all";
+        var highlightConfig = { showInfo: mode === "all", showRulers: WebInspector.settings.showMetricsRulers.get() };
+        if (mode === "all" || mode === "content")
+            highlightConfig.contentColor = WebInspector.Color.PageHighlight.Content.toProtocolRGBA();
+
+        if (mode === "all" || mode === "padding")
+            highlightConfig.paddingColor = WebInspector.Color.PageHighlight.Padding.toProtocolRGBA();
+
+        if (mode === "all" || mode === "border")
+            highlightConfig.borderColor = WebInspector.Color.PageHighlight.Border.toProtocolRGBA();
+
+        if (mode === "all" || mode === "margin")
+            highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margin.toProtocolRGBA();
+
+        if (mode === "all")
+            highlightConfig.eventTargetColor = WebInspector.Color.PageHighlight.EventTarget.toProtocolRGBA();
+
+        return highlightConfig;
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     * @param {function(?Protocol.Error)=} callback
+     * @return {function(?Protocol.Error)}
+     */
+    _markRevision: function(node, callback)
+    {
+        function wrapperFunction(error)
+        {
+            if (!error)
+                this.markUndoableState();
+
+            if (callback)
+                callback.apply(this, arguments);
+        }
+        return wrapperFunction.bind(this);
+    },
+
+    _emulateTouchEventsChanged: function()
+    {
+        const injectedFunction = function() {
+            const touchEvents = ["ontouchstart", "ontouchend", "ontouchmove", "ontouchcancel"];
+            var recepients = [window.__proto__, document.__proto__];
+            for (var i = 0; i < touchEvents.length; ++i) {
+                for (var j = 0; j < recepients.length; ++j) {
+                    if (!(touchEvents[i] in recepients[j]))
+                        Object.defineProperty(recepients[j], touchEvents[i], { value: null, writable: true, configurable: true, enumerable: true });
+                }
+            }
+        }
+
+        var emulationEnabled = WebInspector.settings.emulateTouchEvents.get();
+        if (emulationEnabled && !this._addTouchEventsScriptInjecting) {
+            this._addTouchEventsScriptInjecting = true;
+            PageAgent.addScriptToEvaluateOnLoad("(" + injectedFunction.toString() + ")()", scriptAddedCallback.bind(this));
+        } else {
+            if (typeof this._addTouchEventsScriptId !== "undefined") {
+                PageAgent.removeScriptToEvaluateOnLoad(this._addTouchEventsScriptId);
+                delete this._addTouchEventsScriptId;
+            }
+        }
+
+        function scriptAddedCallback(error, scriptId)
+        {
+            delete this._addTouchEventsScriptInjecting;
+            if (error)
+                return;
+            this._addTouchEventsScriptId = scriptId;
+        }
+
+        PageAgent.setTouchEmulationEnabled(emulationEnabled);
+    },
+
+    markUndoableState: function()
+    {
+        DOMAgent.markUndoableState();
+    },
+
+    /**
+     * @param {function(?Protocol.Error)=} callback
+     */
+    undo: function(callback)
+    {
+        function mycallback(error)
+        {
+            this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoCompleted);
+            callback(error);
+        }
+
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoRequested);
+        DOMAgent.undo(callback);
+    },
+
+    /**
+     * @param {function(?Protocol.Error)=} callback
+     */
+    redo: function(callback)
+    {
+        function mycallback(error)
+        {
+            this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoCompleted);
+            callback(error);
+        }
+
+        this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoRequested);
+        DOMAgent.redo(callback);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {DOMAgent.Dispatcher}
+ * @param {WebInspector.DOMAgent} domAgent
+ */
+WebInspector.DOMDispatcher = function(domAgent)
+{
+    this._domAgent = domAgent;
+}
+
+WebInspector.DOMDispatcher.prototype = {
+    documentUpdated: function()
+    {
+        this._domAgent._documentUpdated();
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     */
+    inspectNodeRequested: function(nodeId)
+    {
+        this._domAgent._inspectNodeRequested(nodeId);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} name
+     * @param {string} value
+     */
+    attributeModified: function(nodeId, name, value)
+    {
+        this._domAgent._attributeModified(nodeId, name, value);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} name
+     */
+    attributeRemoved: function(nodeId, name)
+    {
+        this._domAgent._attributeRemoved(nodeId, name);
+    },
+
+    /**
+     * @param {Array.<DOMAgent.NodeId>} nodeIds
+     */
+    inlineStyleInvalidated: function(nodeIds)
+    {
+        this._domAgent._inlineStyleInvalidated(nodeIds);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} characterData
+     */
+    characterDataModified: function(nodeId, characterData)
+    {
+        this._domAgent._characterDataModified(nodeId, characterData);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} parentId
+     * @param {Array.<DOMAgent.Node>} payloads
+     */
+    setChildNodes: function(parentId, payloads)
+    {
+        this._domAgent._setChildNodes(parentId, payloads);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {number} childNodeCount
+     */
+    childNodeCountUpdated: function(nodeId, childNodeCount)
+    {
+        this._domAgent._childNodeCountUpdated(nodeId, childNodeCount);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} parentNodeId
+     * @param {DOMAgent.NodeId} previousNodeId
+     * @param {DOMAgent.Node} payload
+     */
+    childNodeInserted: function(parentNodeId, previousNodeId, payload)
+    {
+        this._domAgent._childNodeInserted(parentNodeId, previousNodeId, payload);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} parentNodeId
+     * @param {DOMAgent.NodeId} nodeId
+     */
+    childNodeRemoved: function(parentNodeId, nodeId)
+    {
+        this._domAgent._childNodeRemoved(parentNodeId, nodeId);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} hostId
+     * @param {DOMAgent.Node} root
+     */
+    shadowRootPushed: function(hostId, root)
+    {
+        this._domAgent._childNodeInserted(hostId, 0, root);
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} hostId
+     * @param {DOMAgent.NodeId} rootId
+     */
+    shadowRootPopped: function(hostId, rootId)
+    {
+        this._domAgent._childNodeRemoved(hostId, rootId);
+    }
+}
+
+/**
+ * @type {?WebInspector.DOMAgent}
+ */
+WebInspector.domAgent = null;
diff --git a/Source/devtools/front_end/DOMBreakpointsSidebarPane.js b/Source/devtools/front_end/DOMBreakpointsSidebarPane.js
new file mode 100644
index 0000000..68f1328
--- /dev/null
+++ b/Source/devtools/front_end/DOMBreakpointsSidebarPane.js
@@ -0,0 +1,402 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.NativeBreakpointsSidebarPane}
+ */
+WebInspector.DOMBreakpointsSidebarPane = function()
+{
+    WebInspector.NativeBreakpointsSidebarPane.call(this, WebInspector.UIString("DOM Breakpoints"));
+
+    this._breakpointElements = {};
+
+    this._breakpointTypes = {
+        SubtreeModified: "subtree-modified",
+        AttributeModified: "attribute-modified",
+        NodeRemoved: "node-removed"
+    };
+    this._breakpointTypeLabels = {};
+    this._breakpointTypeLabels[this._breakpointTypes.SubtreeModified] = WebInspector.UIString("Subtree Modified");
+    this._breakpointTypeLabels[this._breakpointTypes.AttributeModified] = WebInspector.UIString("Attribute Modified");
+    this._breakpointTypeLabels[this._breakpointTypes.NodeRemoved] = WebInspector.UIString("Node Removed");
+
+    this._contextMenuLabels = {};
+    this._contextMenuLabels[this._breakpointTypes.SubtreeModified] = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Subtree modifications" : "Subtree Modifications");
+    this._contextMenuLabels[this._breakpointTypes.AttributeModified] = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Attributes modifications" : "Attributes Modifications");
+    this._contextMenuLabels[this._breakpointTypes.NodeRemoved] = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Node removal" : "Node Removal");
+
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._inspectedURLChanged, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.NodeRemoved, this._nodeRemoved, this);
+}
+
+WebInspector.DOMBreakpointsSidebarPane.prototype = {
+    _inspectedURLChanged: function(event)
+    {
+        this._breakpointElements = {};
+        this._reset();
+        var url = event.data;
+        this._inspectedURL = url.removeURLFragment();
+    },
+
+    populateNodeContextMenu: function(node, contextMenu)
+    {
+        var nodeBreakpoints = {};
+        for (var id in this._breakpointElements) {
+            var element = this._breakpointElements[id];
+            if (element._node === node)
+                nodeBreakpoints[element._type] = true;
+        }
+
+        function toggleBreakpoint(type)
+        {
+            if (!nodeBreakpoints[type])
+                this._setBreakpoint(node, type, true);
+            else
+                this._removeBreakpoint(node, type);
+            this._saveBreakpoints();
+        }
+
+        var breakPointSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Break on..."));
+        for (var key in this._breakpointTypes) {
+            var type = this._breakpointTypes[key];
+            var label = this._contextMenuLabels[type];
+            breakPointSubMenu.appendCheckboxItem(label, toggleBreakpoint.bind(this, type), nodeBreakpoints[type]);
+        }
+    },
+
+    createBreakpointHitStatusMessage: function(auxData, callback)
+    {
+        if (auxData.type === this._breakpointTypes.SubtreeModified) {
+            var targetNodeObject = WebInspector.RemoteObject.fromPayload(auxData["targetNode"]);
+            function didPushNodeToFrontend(targetNodeId)
+            {
+                if (targetNodeId)
+                    targetNodeObject.release();
+                this._doCreateBreakpointHitStatusMessage(auxData, targetNodeId, callback);
+            }
+            targetNodeObject.pushNodeToFrontend(didPushNodeToFrontend.bind(this));
+        } else
+            this._doCreateBreakpointHitStatusMessage(auxData, null, callback);
+    },
+
+    _doCreateBreakpointHitStatusMessage: function (auxData, targetNodeId, callback)
+    {
+        var message;
+        var typeLabel = this._breakpointTypeLabels[auxData.type];
+        var linkifiedNode = WebInspector.DOMPresentationUtils.linkifyNodeById(auxData.nodeId);
+        var substitutions = [typeLabel, linkifiedNode];
+        var targetNode = "";
+        if (targetNodeId)
+            targetNode = WebInspector.DOMPresentationUtils.linkifyNodeById(targetNodeId);
+
+        if (auxData.type === this._breakpointTypes.SubtreeModified) {
+            if (auxData.insertion) {
+                if (targetNodeId !== auxData.nodeId) {
+                    message = "Paused on a \"%s\" breakpoint set on %s, because a new child was added to its descendant %s.";
+                    substitutions.push(targetNode);
+                } else
+                    message = "Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node.";
+            } else {
+                message = "Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed.";
+                substitutions.push(targetNode);
+            }
+        } else
+            message = "Paused on a \"%s\" breakpoint set on %s.";
+
+        var element = document.createElement("span");
+        var formatters = {
+            s: function(substitution)
+            {
+                return substitution;
+            }
+        };
+        function append(a, b)
+        {
+            if (typeof b === "string")
+                b = document.createTextNode(b);
+            element.appendChild(b);
+        }
+        WebInspector.formatLocalized(message, substitutions, formatters, "", append);
+
+        callback(element);
+    },
+
+    _nodeRemoved: function(event)
+    {
+        var node = event.data.node;
+        this._removeBreakpointsForNode(event.data.node);
+        if (!node.children)
+            return;
+        for (var i = 0; i < node.children.length; ++i)
+            this._removeBreakpointsForNode(node.children[i]);
+        this._saveBreakpoints();
+    },
+
+    _removeBreakpointsForNode: function(node)
+    {
+        for (var id in this._breakpointElements) {
+            var element = this._breakpointElements[id];
+            if (element._node === node)
+                this._removeBreakpoint(element._node, element._type);
+        }
+    },
+
+    _setBreakpoint: function(node, type, enabled)
+    {
+        var breakpointId = this._createBreakpointId(node.id, type);
+        if (breakpointId in this._breakpointElements)
+            return;
+
+        var element = document.createElement("li");
+        element._node = node;
+        element._type = type;
+        element.addEventListener("contextmenu", this._contextMenu.bind(this, node, type), true);
+
+        var checkboxElement = document.createElement("input");
+        checkboxElement.className = "checkbox-elem";
+        checkboxElement.type = "checkbox";
+        checkboxElement.checked = enabled;
+        checkboxElement.addEventListener("click", this._checkboxClicked.bind(this, node, type), false);
+        element._checkboxElement = checkboxElement;
+        element.appendChild(checkboxElement);
+
+        var labelElement = document.createElement("span");
+        element.appendChild(labelElement);
+
+        var linkifiedNode = WebInspector.DOMPresentationUtils.linkifyNodeById(node.id);
+        linkifiedNode.addStyleClass("monospace");
+        labelElement.appendChild(linkifiedNode);
+
+        var description = document.createElement("div");
+        description.className = "source-text";
+        description.textContent = this._breakpointTypeLabels[type];
+        labelElement.appendChild(description);
+
+        var currentElement = this.listElement.firstChild;
+        while (currentElement) {
+            if (currentElement._type && currentElement._type < element._type)
+                break;
+            currentElement = currentElement.nextSibling;
+        }
+        this._addListElement(element, currentElement);
+        this._breakpointElements[breakpointId] = element;
+        if (enabled)
+            DOMDebuggerAgent.setDOMBreakpoint(node.id, type);
+    },
+
+    _removeAllBreakpoints: function()
+    {
+        for (var id in this._breakpointElements) {
+            var element = this._breakpointElements[id];
+            this._removeBreakpoint(element._node, element._type);
+        }
+        this._saveBreakpoints();
+    },
+
+    _removeBreakpoint: function(node, type)
+    {
+        var breakpointId = this._createBreakpointId(node.id, type);
+        var element = this._breakpointElements[breakpointId];
+        if (!element)
+            return;
+
+        this._removeListElement(element);
+        delete this._breakpointElements[breakpointId];
+        if (element._checkboxElement.checked)
+            DOMDebuggerAgent.removeDOMBreakpoint(node.id, type);
+    },
+
+    _contextMenu: function(node, type, event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        function removeBreakpoint()
+        {
+            this._removeBreakpoint(node, type);
+            this._saveBreakpoints();
+        }
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove breakpoint" : "Remove Breakpoint"), removeBreakpoint.bind(this));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all DOM breakpoints" : "Remove All DOM Breakpoints"), this._removeAllBreakpoints.bind(this));
+        contextMenu.show();
+    },
+
+    _checkboxClicked: function(node, type, event)
+    {
+        if (event.target.checked)
+            DOMDebuggerAgent.setDOMBreakpoint(node.id, type);
+        else
+            DOMDebuggerAgent.removeDOMBreakpoint(node.id, type);
+        this._saveBreakpoints();
+    },
+
+    highlightBreakpoint: function(auxData)
+    {
+        var breakpointId = this._createBreakpointId(auxData.nodeId, auxData.type);
+        var element = this._breakpointElements[breakpointId];
+        if (!element)
+            return;
+        this.expand();
+        element.addStyleClass("breakpoint-hit");
+        this._highlightedElement = element;
+    },
+
+    clearBreakpointHighlight: function()
+    {
+        if (this._highlightedElement) {
+            this._highlightedElement.removeStyleClass("breakpoint-hit");
+            delete this._highlightedElement;
+        }
+    },
+
+    _createBreakpointId: function(nodeId, type)
+    {
+        return nodeId + ":" + type;
+    },
+
+    _saveBreakpoints: function()
+    {
+        var breakpoints = [];
+        var storedBreakpoints = WebInspector.settings.domBreakpoints.get();
+        for (var i = 0; i < storedBreakpoints.length; ++i) {
+            var breakpoint = storedBreakpoints[i];
+            if (breakpoint.url !== this._inspectedURL)
+                breakpoints.push(breakpoint);
+        }
+        for (var id in this._breakpointElements) {
+            var element = this._breakpointElements[id];
+            breakpoints.push({ url: this._inspectedURL, path: element._node.path(), type: element._type, enabled: element._checkboxElement.checked });
+        }
+        WebInspector.settings.domBreakpoints.set(breakpoints);
+    },
+
+    restoreBreakpoints: function()
+    {
+        var pathToBreakpoints = {};
+
+        function didPushNodeByPathToFrontend(path, nodeId)
+        {
+            var node = WebInspector.domAgent.nodeForId(nodeId);
+            if (!node)
+                return;
+
+            var breakpoints = pathToBreakpoints[path];
+            for (var i = 0; i < breakpoints.length; ++i)
+                this._setBreakpoint(node, breakpoints[i].type, breakpoints[i].enabled);
+        }
+
+        var breakpoints = WebInspector.settings.domBreakpoints.get();
+        for (var i = 0; i < breakpoints.length; ++i) {
+            var breakpoint = breakpoints[i];
+            if (breakpoint.url !== this._inspectedURL)
+                continue;
+            var path = breakpoint.path;
+            if (!pathToBreakpoints[path]) {
+                pathToBreakpoints[path] = [];
+                WebInspector.domAgent.pushNodeByPathToFrontend(path, didPushNodeByPathToFrontend.bind(this, path));
+            }
+            pathToBreakpoints[path].push(breakpoint);
+        }
+    },
+
+    /**
+     * @param {WebInspector.Panel} panel
+     */
+    createProxy: function(panel)
+    {
+        var proxy = new WebInspector.DOMBreakpointsSidebarPane.Proxy(this, panel);
+        if (!this._proxies)
+            this._proxies = [];
+        this._proxies.push(proxy);
+        return proxy;
+    },
+
+    onContentReady: function()
+    {
+        for (var i = 0; i != this._proxies.length; i++)
+            this._proxies[i].onContentReady();
+    },
+
+    __proto__: WebInspector.NativeBreakpointsSidebarPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ * @param {WebInspector.DOMBreakpointsSidebarPane} pane
+ * @param {WebInspector.Panel} panel
+ */
+WebInspector.DOMBreakpointsSidebarPane.Proxy = function(pane, panel)
+{
+    WebInspector.View._assert(!pane.titleElement.firstChild, "Cannot create proxy for a sidebar pane with a toolbar");
+
+    WebInspector.SidebarPane.call(this, pane.title());
+    this.registerRequiredCSS("breakpointsList.css");
+
+    this._wrappedPane = pane;
+    this._panel = panel;
+
+    this.bodyElement.removeSelf();
+    this.bodyElement = this._wrappedPane.bodyElement;
+}
+
+WebInspector.DOMBreakpointsSidebarPane.Proxy.prototype = {
+    expand: function()
+    {
+        this._wrappedPane.expand();
+    },
+
+    onContentReady: function()
+    {
+        if (!this._panel.isShowing())
+            return;
+
+        this._reattachBody();
+        WebInspector.SidebarPane.prototype.onContentReady.call(this);
+    },
+
+    wasShown: function()
+    {
+        WebInspector.SidebarPane.prototype.wasShown.call(this);
+        this._reattachBody();
+    },
+
+    _reattachBody: function()
+    {
+        if (this.bodyElement.parentNode !== this.element)
+            this.element.appendChild(this.bodyElement);
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
+
+/**
+ * @type {?WebInspector.DOMBreakpointsSidebarPane}
+ */
+WebInspector.domBreakpointsSidebarPane = null;
diff --git a/Source/devtools/front_end/DOMCountersGraph.js b/Source/devtools/front_end/DOMCountersGraph.js
new file mode 100644
index 0000000..1f004a4
--- /dev/null
+++ b/Source/devtools/front_end/DOMCountersGraph.js
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.MemoryStatistics}
+ * @param {WebInspector.TimelinePanel} timelinePanel
+ * @param {WebInspector.TimelineModel} model
+ * @param {number} sidebarWidth
+ */
+WebInspector.DOMCountersGraph = function(timelinePanel, model, sidebarWidth)
+{
+    WebInspector.MemoryStatistics.call(this, timelinePanel, model, sidebarWidth);
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.CounterUIBase}
+ * @param {WebInspector.DOMCountersGraph} memoryCountersPane
+ * @param {string} title
+ * @param {string} currentValueLabel
+ * @param {Array.<number>} rgb
+ * @param {function(WebInspector.DOMCountersGraph.Counter):number} valueGetter
+ */
+WebInspector.DOMCounterUI = function(memoryCountersPane, title, currentValueLabel, rgb, valueGetter)
+{
+    var swatchColor = "rgb(" + rgb.join(",") + ")";
+    WebInspector.CounterUIBase.call(this, memoryCountersPane, title, swatchColor, valueGetter)
+    this._range = this._swatch.element.createChild("span");
+
+    this._value = memoryCountersPane._currentValuesBar.createChild("span", "memory-counter-value");
+    this._value.style.color = swatchColor;
+    this._currentValueLabel = currentValueLabel;
+
+    this.graphColor = "rgba(" + rgb.join(",") + ",0.8)";
+    this.graphYValues = [];
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.MemoryStatistics.Counter}
+ * @param {number} time
+ * @param {number} documentCount
+ * @param {number} nodeCount
+ * @param {number} listenerCount
+ */
+WebInspector.DOMCountersGraph.Counter = function(time, documentCount, nodeCount, listenerCount)
+{
+    WebInspector.MemoryStatistics.Counter.call(this, time);
+    this.documentCount = documentCount;
+    this.nodeCount = nodeCount;
+    this.listenerCount = listenerCount;
+}
+
+WebInspector.DOMCounterUI.prototype = {
+    /**
+     * @param {number} minValue
+     * @param {number} maxValue
+     */
+    setRange: function(minValue, maxValue)
+    {
+        this._range.textContent = WebInspector.UIString("[ %d - %d ]", minValue, maxValue);
+    },
+
+    updateCurrentValue: function(countersEntry)
+    {
+        this._value.textContent =  WebInspector.UIString(this._currentValueLabel, this.valueGetter(countersEntry));
+    },
+
+    clearCurrentValueAndMarker: function(ctx)
+    {
+        this._value.textContent = "";
+        this.restoreImageUnderMarker(ctx);
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     * @param {number} x
+     * @param {number} y
+     * @param {number} radius
+     */
+    saveImageUnderMarker: function(ctx, x, y, radius)
+    {
+        const w = radius + 1;
+        var imageData = ctx.getImageData(x - w, y - w, 2 * w, 2 * w);
+        this._imageUnderMarker = {
+            x: x - w,
+            y: y - w,
+            imageData: imageData
+        };
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     */
+    restoreImageUnderMarker: function(ctx)
+    {
+        if (!this.visible)
+            return;
+        if (this._imageUnderMarker)
+            ctx.putImageData(this._imageUnderMarker.imageData, this._imageUnderMarker.x, this._imageUnderMarker.y);
+        this.discardImageUnderMarker();
+    },
+
+    discardImageUnderMarker: function()
+    {
+        delete this._imageUnderMarker;
+    },
+
+    __proto__: WebInspector.CounterUIBase.prototype
+}
+
+
+WebInspector.DOMCountersGraph.prototype = {
+    _createCurrentValuesBar: function()
+    {
+        this._currentValuesBar = this._canvasContainer.createChild("div");
+        this._currentValuesBar.id = "counter-values-bar";
+        this._canvasContainer.addStyleClass("dom-counters");
+    },
+
+    /**
+     * @return {Array.<WebInspector.DOMCounterUI>}
+     */
+    _createCounterUIList: function()
+    {
+        function getDocumentCount(entry)
+        {
+            return entry.documentCount;
+        }
+        function getNodeCount(entry)
+        {
+            return entry.nodeCount;
+        }
+        function getListenerCount(entry)
+        {
+            return entry.listenerCount;
+        }
+        return [
+            new WebInspector.DOMCounterUI(this, "Document Count", "Documents: %d", [100, 0, 0], getDocumentCount),
+            new WebInspector.DOMCounterUI(this, "DOM Node Count", "Nodes: %d", [0, 100, 0], getNodeCount),
+            new WebInspector.DOMCounterUI(this, "Event Listener Count", "Listeners: %d", [0, 0, 100], getListenerCount)
+        ];
+    },
+
+    _canvasHeight: function()
+    {
+        return this._canvasContainer.offsetHeight - this._currentValuesBar.offsetHeight;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onRecordAdded: function(event)
+    {
+        function addStatistics(record)
+        {
+            var counters = record["counters"];
+            if (!counters)
+                return;
+            this._counters.push(new WebInspector.DOMCountersGraph.Counter(
+                record.endTime || record.startTime,
+                counters["documents"],
+                counters["nodes"],
+                counters["jsEventListeners"]
+            ));
+        }
+        WebInspector.TimelinePresentationModel.forAllRecords([event.data], null, addStatistics.bind(this));
+    },
+
+    _draw: function()
+    {
+        WebInspector.MemoryStatistics.prototype._draw.call(this);
+        for (var i = 0; i < this._counterUI.length; i++)
+            this._drawGraph(this._counterUI[i]);
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     */
+    _restoreImageUnderMarker: function(ctx)
+    {
+        for (var i = 0; i < this._counterUI.length; i++) {
+            var counterUI = this._counterUI[i];
+            if (!counterUI.visible)
+                continue;
+            counterUI.restoreImageUnderMarker(ctx);
+        }
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     * @param {number} x
+     * @param {number} index
+     */
+    _saveImageUnderMarker: function(ctx, x, index)
+    {
+        const radius = 2;
+        for (var i = 0; i < this._counterUI.length; i++) {
+            var counterUI = this._counterUI[i];
+            if (!counterUI.visible)
+                continue;
+            var y = counterUI.graphYValues[index];
+            counterUI.saveImageUnderMarker(ctx, x, y, radius);
+        }
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     * @param {number} x
+     * @param {number} index
+     */
+    _drawMarker: function(ctx, x, index)
+    {
+        this._saveImageUnderMarker(ctx, x, index);
+        const radius = 2;
+        for (var i = 0; i < this._counterUI.length; i++) {
+            var counterUI = this._counterUI[i];
+            if (!counterUI.visible)
+                continue;
+            var y = counterUI.graphYValues[index];
+            ctx.beginPath();
+            ctx.arc(x, y, radius, 0, Math.PI * 2, true);
+            ctx.lineWidth = 1;
+            ctx.fillStyle = counterUI.graphColor;
+            ctx.strokeStyle = counterUI.graphColor;
+            ctx.fill();
+            ctx.stroke();
+            ctx.closePath();
+        }
+    },
+
+    /**
+     * @param {WebInspector.CounterUIBase} counterUI
+     */
+    _drawGraph: function(counterUI)
+    {
+        var canvas = this._canvas;
+        var ctx = canvas.getContext("2d");
+        var width = canvas.width;
+        var height = this._clippedHeight;
+        var originY = this._originY;
+        var valueGetter = counterUI.valueGetter;
+
+        if (!this._counters.length)
+            return;
+
+        var maxValue;
+        var minValue;
+        for (var i = this._minimumIndex; i <= this._maximumIndex; i++) {
+            var value = valueGetter(this._counters[i]);
+            if (minValue === undefined || value < minValue)
+                minValue = value;
+            if (maxValue === undefined || value > maxValue)
+                maxValue = value;
+        }
+
+        counterUI.setRange(minValue, maxValue);
+
+        if (!counterUI.visible)
+            return;
+
+        var yValues = counterUI.graphYValues;
+        yValues.length = this._counters.length;
+
+        var maxYRange = maxValue - minValue;
+        var yFactor = maxYRange ? height / (maxYRange) : 1;
+
+        ctx.beginPath();
+        var currentY = originY + (height - (valueGetter(this._counters[this._minimumIndex]) - minValue) * yFactor);
+        ctx.moveTo(0, currentY);
+        for (var i = this._minimumIndex; i <= this._maximumIndex; i++) {
+             var x = this._counters[i].x;
+             ctx.lineTo(x, currentY);
+             currentY = originY + (height - (valueGetter(this._counters[i]) - minValue) * yFactor);
+             ctx.lineTo(x, currentY);
+
+             yValues[i] = currentY;
+        }
+        ctx.lineTo(width, currentY);
+        ctx.lineWidth = 1;
+        ctx.strokeStyle = counterUI.graphColor;
+        ctx.stroke();
+        ctx.closePath();
+    },
+
+    _discardImageUnderMarker: function()
+    {
+        for (var i = 0; i < this._counterUI.length; i++)
+            this._counterUI[i].discardImageUnderMarker();
+    },
+
+    __proto__: WebInspector.MemoryStatistics.prototype
+}
+
diff --git a/Source/devtools/front_end/DOMExtension.js b/Source/devtools/front_end/DOMExtension.js
new file mode 100644
index 0000000..f8ddbab
--- /dev/null
+++ b/Source/devtools/front_end/DOMExtension.js
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2007 Apple Inc.  All rights reserved.
+ * Copyright (C) 2012 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ *
+ * Contains diff method based on Javascript Diff Algorithm By John Resig
+ * http://ejohn.org/files/jsdiff.js (released under the MIT license).
+ */
+
+/**
+ * @param {string=} direction
+ */
+Node.prototype.rangeOfWord = function(offset, stopCharacters, stayWithinNode, direction)
+{
+    var startNode;
+    var startOffset = 0;
+    var endNode;
+    var endOffset = 0;
+
+    if (!stayWithinNode)
+        stayWithinNode = this;
+
+    if (!direction || direction === "backward" || direction === "both") {
+        var node = this;
+        while (node) {
+            if (node === stayWithinNode) {
+                if (!startNode)
+                    startNode = stayWithinNode;
+                break;
+            }
+
+            if (node.nodeType === Node.TEXT_NODE) {
+                var start = (node === this ? (offset - 1) : (node.nodeValue.length - 1));
+                for (var i = start; i >= 0; --i) {
+                    if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
+                        startNode = node;
+                        startOffset = i + 1;
+                        break;
+                    }
+                }
+            }
+
+            if (startNode)
+                break;
+
+            node = node.traversePreviousNode(stayWithinNode);
+        }
+
+        if (!startNode) {
+            startNode = stayWithinNode;
+            startOffset = 0;
+        }
+    } else {
+        startNode = this;
+        startOffset = offset;
+    }
+
+    if (!direction || direction === "forward" || direction === "both") {
+        node = this;
+        while (node) {
+            if (node === stayWithinNode) {
+                if (!endNode)
+                    endNode = stayWithinNode;
+                break;
+            }
+
+            if (node.nodeType === Node.TEXT_NODE) {
+                var start = (node === this ? offset : 0);
+                for (var i = start; i < node.nodeValue.length; ++i) {
+                    if (stopCharacters.indexOf(node.nodeValue[i]) !== -1) {
+                        endNode = node;
+                        endOffset = i;
+                        break;
+                    }
+                }
+            }
+
+            if (endNode)
+                break;
+
+            node = node.traverseNextNode(stayWithinNode);
+        }
+
+        if (!endNode) {
+            endNode = stayWithinNode;
+            endOffset = stayWithinNode.nodeType === Node.TEXT_NODE ? stayWithinNode.nodeValue.length : stayWithinNode.childNodes.length;
+        }
+    } else {
+        endNode = this;
+        endOffset = offset;
+    }
+
+    var result = this.ownerDocument.createRange();
+    result.setStart(startNode, startOffset);
+    result.setEnd(endNode, endOffset);
+
+    return result;
+}
+
+Node.prototype.traverseNextTextNode = function(stayWithin)
+{
+    var node = this.traverseNextNode(stayWithin);
+    if (!node)
+        return;
+
+    while (node && node.nodeType !== Node.TEXT_NODE)
+        node = node.traverseNextNode(stayWithin);
+
+    return node;
+}
+
+Node.prototype.rangeBoundaryForOffset = function(offset)
+{
+    var node = this.traverseNextTextNode(this);
+    while (node && offset > node.nodeValue.length) {
+        offset -= node.nodeValue.length;
+        node = node.traverseNextTextNode(this);
+    }
+    if (!node)
+        return { container: this, offset: 0 };
+    return { container: node, offset: offset };
+}
+
+/**
+ * @param {string} className
+ */
+Element.prototype.removeStyleClass = function(className)
+{
+    this.classList.remove(className);
+}
+
+Element.prototype.removeMatchingStyleClasses = function(classNameRegex)
+{
+    var regex = new RegExp("(^|\\s+)" + classNameRegex + "($|\\s+)");
+    if (regex.test(this.className))
+        this.className = this.className.replace(regex, " ");
+}
+
+/**
+ * @param {string} className
+ */
+Element.prototype.addStyleClass = function(className)
+{
+    this.classList.add(className);
+}
+
+/**
+ * @param {string} className
+ * @return {boolean}
+ */
+Element.prototype.hasStyleClass = function(className)
+{
+    return this.classList.contains(className);
+}
+
+/**
+ * @param {string} className
+ * @param {*} enable
+ */
+Element.prototype.enableStyleClass = function(className, enable)
+{
+    if (enable)
+        this.addStyleClass(className);
+    else
+        this.removeStyleClass(className);
+}
+
+/**
+ * @param {number|undefined} x
+ * @param {number|undefined} y
+ */
+Element.prototype.positionAt = function(x, y)
+{
+    if (typeof x === "number")
+        this.style.setProperty("left", x + "px");
+    else
+        this.style.removeProperty("left");
+
+    if (typeof y === "number")
+        this.style.setProperty("top", y + "px");
+    else
+        this.style.removeProperty("top");
+}
+
+Element.prototype.isScrolledToBottom = function()
+{
+    // This code works only for 0-width border
+    return this.scrollTop + this.clientHeight === this.scrollHeight;
+}
+
+Element.prototype.removeSelf = function()
+{
+    if (this.parentElement)
+        this.parentElement.removeChild(this);
+}
+
+CharacterData.prototype.removeSelf = Element.prototype.removeSelf;
+DocumentType.prototype.removeSelf = Element.prototype.removeSelf;
+
+/**
+ * @param {Node} fromNode
+ * @param {Node} toNode
+ */
+function removeSubsequentNodes(fromNode, toNode)
+{
+    for (var node = fromNode; node && node !== toNode; ) {
+        var nodeToRemove = node;
+        node = node.nextSibling;
+        nodeToRemove.removeSelf();
+    }
+}
+
+/**
+ * @constructor
+ * @param {number} width
+ * @param {number} height
+ */
+function Size(width, height)
+{
+    this.width = width;
+    this.height = height;
+}
+
+/**
+ * @param {Element=} containerElement
+ * @return {Size}
+ */
+Element.prototype.measurePreferredSize = function(containerElement)
+{
+    containerElement = containerElement || document.body;
+    containerElement.appendChild(this);
+    this.positionAt(0, 0);
+    var result = new Size(this.offsetWidth, this.offsetHeight);
+    this.positionAt(undefined, undefined);
+    this.removeSelf();
+    return result;
+}
+
+Node.prototype.enclosingNodeOrSelfWithNodeNameInArray = function(nameArray)
+{
+    for (var node = this; node && node !== this.ownerDocument; node = node.parentNode)
+        for (var i = 0; i < nameArray.length; ++i)
+            if (node.nodeName.toLowerCase() === nameArray[i].toLowerCase())
+                return node;
+    return null;
+}
+
+Node.prototype.enclosingNodeOrSelfWithNodeName = function(nodeName)
+{
+    return this.enclosingNodeOrSelfWithNodeNameInArray([nodeName]);
+}
+
+/**
+ * @param {string} className
+ * @param {Element=} stayWithin
+ */
+Node.prototype.enclosingNodeOrSelfWithClass = function(className, stayWithin)
+{
+    for (var node = this; node && node !== stayWithin && node !== this.ownerDocument; node = node.parentNode)
+        if (node.nodeType === Node.ELEMENT_NODE && node.hasStyleClass(className))
+            return node;
+    return null;
+}
+
+Element.prototype.query = function(query)
+{
+    return this.ownerDocument.evaluate(query, this, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
+}
+
+Element.prototype.removeChildren = function()
+{
+    if (this.firstChild)
+        this.textContent = "";
+}
+
+Element.prototype.isInsertionCaretInside = function()
+{
+    var selection = window.getSelection();
+    if (!selection.rangeCount || !selection.isCollapsed)
+        return false;
+    var selectionRange = selection.getRangeAt(0);
+    return selectionRange.startContainer.isSelfOrDescendant(this);
+}
+
+/**
+ * @param {string=} className
+ */
+Element.prototype.createChild = function(elementName, className)
+{
+    var element = this.ownerDocument.createElement(elementName);
+    if (className)
+        element.className = className;
+    this.appendChild(element);
+    return element;
+}
+
+DocumentFragment.prototype.createChild = Element.prototype.createChild;
+
+/**
+ * @param {string} text
+ */
+Element.prototype.createTextChild = function(text)
+{
+    var element = this.ownerDocument.createTextNode(text);
+    this.appendChild(element);
+    return element;
+}
+
+DocumentFragment.prototype.createTextChild = Element.prototype.createTextChild;
+
+/**
+ * @return {number}
+ */
+Element.prototype.totalOffsetLeft = function()
+{
+    return this.totalOffset().left;
+}
+
+/**
+ * @return {number}
+ */
+Element.prototype.totalOffsetTop = function()
+{
+    return this.totalOffset().top;
+
+}
+
+Element.prototype.totalOffset = function()
+{
+    var totalLeft = 0;
+    var totalTop = 0;
+
+    for (var element = this; element; element = element.offsetParent) {
+        totalLeft += element.offsetLeft;
+        totalTop += element.offsetTop;
+        if (this !== element) {
+            totalLeft += element.clientLeft - element.scrollLeft;
+            totalTop += element.clientTop - element.scrollTop;
+        }
+    }
+
+    return { left: totalLeft, top: totalTop };
+}
+
+Element.prototype.scrollOffset = function()
+{
+    var curLeft = 0;
+    var curTop = 0;
+    for (var element = this; element; element = element.scrollParent) {
+        curLeft += element.scrollLeft;
+        curTop += element.scrollTop;
+    }
+    return { left: curLeft, top: curTop };
+}
+
+/**
+ * @constructor
+ * @param {number=} x
+ * @param {number=} y
+ * @param {number=} width
+ * @param {number=} height
+ */
+function AnchorBox(x, y, width, height)
+{
+    this.x = x || 0;
+    this.y = y || 0;
+    this.width = width || 0;
+    this.height = height || 0;
+}
+
+/**
+ * @param {Window} targetWindow
+ * @return {AnchorBox}
+ */
+Element.prototype.offsetRelativeToWindow = function(targetWindow)
+{
+    var elementOffset = new AnchorBox();
+    var curElement = this;
+    var curWindow = this.ownerDocument.defaultView;
+    while (curWindow && curElement) {
+        elementOffset.x += curElement.totalOffsetLeft();
+        elementOffset.y += curElement.totalOffsetTop();
+        if (curWindow === targetWindow)
+            break;
+
+        curElement = curWindow.frameElement;
+        curWindow = curWindow.parent;
+    }
+
+    return elementOffset;
+}
+
+/**
+ * @param {Window} targetWindow
+ * @return {AnchorBox}
+ */
+Element.prototype.boxInWindow = function(targetWindow)
+{
+    targetWindow = targetWindow || this.ownerDocument.defaultView;
+
+    var anchorBox = this.offsetRelativeToWindow(window);
+    anchorBox.width = Math.min(this.offsetWidth, window.innerWidth - anchorBox.x);
+    anchorBox.height = Math.min(this.offsetHeight, window.innerHeight - anchorBox.y);
+
+    return anchorBox;
+}
+
+/**
+ * @param {string} text
+ */
+Element.prototype.setTextAndTitle = function(text)
+{
+    this.textContent = text;
+    this.title = text;
+}
+
+KeyboardEvent.prototype.__defineGetter__("data", function()
+{
+    // Emulate "data" attribute from DOM 3 TextInput event.
+    // See http://www.w3.org/TR/DOM-Level-3-Events/#events-Events-TextEvent-data
+    switch (this.type) {
+        case "keypress":
+            if (!this.ctrlKey && !this.metaKey)
+                return String.fromCharCode(this.charCode);
+            else
+                return "";
+        case "keydown":
+        case "keyup":
+            if (!this.ctrlKey && !this.metaKey && !this.altKey)
+                return String.fromCharCode(this.which);
+            else
+                return "";
+    }
+});
+
+/**
+ * @param {boolean=} preventDefault
+ */
+Event.prototype.consume = function(preventDefault)
+{
+    this.stopImmediatePropagation();
+    if (preventDefault)
+        this.preventDefault();
+    this.handled = true;
+}
+
+Text.prototype.select = function(start, end)
+{
+    start = start || 0;
+    end = end || this.textContent.length;
+
+    if (start < 0)
+        start = end + start;
+
+    var selection = this.ownerDocument.defaultView.getSelection();
+    selection.removeAllRanges();
+    var range = this.ownerDocument.createRange();
+    range.setStart(this, start);
+    range.setEnd(this, end);
+    selection.addRange(range);
+    return this;
+}
+
+Element.prototype.selectionLeftOffset = function()
+{
+    // Calculate selection offset relative to the current element.
+
+    var selection = window.getSelection();
+    if (!selection.containsNode(this, true))
+        return null;
+
+    var leftOffset = selection.anchorOffset;
+    var node = selection.anchorNode;
+
+    while (node !== this) {
+        while (node.previousSibling) {
+            node = node.previousSibling;
+            leftOffset += node.textContent.length;
+        }
+        node = node.parentNode;
+    }
+
+    return leftOffset;
+}
+
+Node.prototype.isAncestor = function(node)
+{
+    if (!node)
+        return false;
+
+    var currentNode = node.parentNode;
+    while (currentNode) {
+        if (this === currentNode)
+            return true;
+        currentNode = currentNode.parentNode;
+    }
+    return false;
+}
+
+Node.prototype.isDescendant = function(descendant)
+{
+    return !!descendant && descendant.isAncestor(this);
+}
+
+Node.prototype.isSelfOrAncestor = function(node)
+{
+    return !!node && (node === this || this.isAncestor(node));
+}
+
+Node.prototype.isSelfOrDescendant = function(node)
+{
+    return !!node && (node === this || this.isDescendant(node));
+}
+
+Node.prototype.traverseNextNode = function(stayWithin)
+{
+    var node = this.firstChild;
+    if (node)
+        return node;
+
+    if (stayWithin && this === stayWithin)
+        return null;
+
+    node = this.nextSibling;
+    if (node)
+        return node;
+
+    node = this;
+    while (node && !node.nextSibling && (!stayWithin || !node.parentNode || node.parentNode !== stayWithin))
+        node = node.parentNode;
+    if (!node)
+        return null;
+
+    return node.nextSibling;
+}
+
+Node.prototype.traversePreviousNode = function(stayWithin)
+{
+    if (stayWithin && this === stayWithin)
+        return null;
+    var node = this.previousSibling;
+    while (node && node.lastChild)
+        node = node.lastChild;
+    if (node)
+        return node;
+    return this.parentNode;
+}
+
+function isEnterKey(event) {
+    // Check if in IME.
+    return event.keyCode !== 229 && event.keyIdentifier === "Enter";
+}
+
+function consumeEvent(e)
+{
+    e.consume();
+}
+
+/**
+ * Mutation observers leak memory. Keep track of them and disconnect
+ * on unload.
+ * @constructor
+ * @param {function(Array.<WebKitMutation>)} handler
+ */
+function NonLeakingMutationObserver(handler)
+{
+    this._observer = new WebKitMutationObserver(handler);
+    NonLeakingMutationObserver._instances.push(this);
+    if (!NonLeakingMutationObserver._unloadListener) {
+        NonLeakingMutationObserver._unloadListener = function() {
+            while (NonLeakingMutationObserver._instances.length)
+                NonLeakingMutationObserver._instances[NonLeakingMutationObserver._instances.length - 1].disconnect();
+        };
+        window.addEventListener("unload", NonLeakingMutationObserver._unloadListener, false);
+    }
+}
+
+NonLeakingMutationObserver._instances = [];
+
+NonLeakingMutationObserver.prototype = {
+    /**
+     * @param {Element} element
+     * @param {Object} config
+     */
+    observe: function(element, config)
+    {
+        if (this._observer)
+            this._observer.observe(element, config);
+    },
+
+    disconnect: function()
+    {
+        if (this._observer)
+            this._observer.disconnect();
+        NonLeakingMutationObserver._instances.remove(this);
+        delete this._observer;
+    }
+}
+
diff --git a/Source/devtools/front_end/DOMPresentationUtils.js b/Source/devtools/front_end/DOMPresentationUtils.js
new file mode 100644
index 0000000..0f9f483
--- /dev/null
+++ b/Source/devtools/front_end/DOMPresentationUtils.js
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 Google Inc.  All rights reserved.
+ * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
+ * 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 Computer, 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.DOMPresentationUtils = {}
+
+WebInspector.DOMPresentationUtils.decorateNodeLabel = function(node, parentElement)
+{
+    var title = node.nodeNameInCorrectCase();
+
+    var nameElement = document.createElement("span");
+    nameElement.textContent = title;
+    parentElement.appendChild(nameElement);
+
+    var idAttribute = node.getAttribute("id");
+    if (idAttribute) {
+        var idElement = document.createElement("span");
+        parentElement.appendChild(idElement);
+
+        var part = "#" + idAttribute;
+        title += part;
+        idElement.appendChild(document.createTextNode(part));
+
+        // Mark the name as extra, since the ID is more important.
+        nameElement.className = "extra";
+    }
+
+    var classAttribute = node.getAttribute("class");
+    if (classAttribute) {
+        var classes = classAttribute.split(/\s+/);
+        var foundClasses = {};
+
+        if (classes.length) {
+            var classesElement = document.createElement("span");
+            classesElement.className = "extra";
+            parentElement.appendChild(classesElement);
+
+            for (var i = 0; i < classes.length; ++i) {
+                var className = classes[i];
+                if (className && !(className in foundClasses)) {
+                    var part = "." + className;
+                    title += part;
+                    classesElement.appendChild(document.createTextNode(part));
+                    foundClasses[className] = true;
+                }
+            }
+        }
+    }
+    parentElement.title = title;
+}
+
+/**
+ * @param {Element} container
+ * @param {string} nodeTitle
+ */
+WebInspector.DOMPresentationUtils.createSpansForNodeTitle = function(container, nodeTitle)
+{
+    var match = nodeTitle.match(/([^#.]+)(#[^.]+)?(\..*)?/);
+    container.createChild("span", "webkit-html-tag-name").textContent = match[1];
+    if (match[2])
+        container.createChild("span", "webkit-html-attribute-value").textContent = match[2];
+    if (match[3])
+        container.createChild("span", "webkit-html-attribute-name").textContent = match[3];
+}
+
+WebInspector.DOMPresentationUtils.linkifyNodeReference = function(node)
+{
+    var link = document.createElement("span");
+    link.className = "node-link";
+    WebInspector.DOMPresentationUtils.decorateNodeLabel(node, link);
+
+    link.addEventListener("click", WebInspector.domAgent.inspectElement.bind(WebInspector.domAgent, node.id), false);
+    link.addEventListener("mouseover", WebInspector.domAgent.highlightDOMNode.bind(WebInspector.domAgent, node.id, "", undefined), false);
+    link.addEventListener("mouseout", WebInspector.domAgent.hideDOMNodeHighlight.bind(WebInspector.domAgent), false);
+
+    return link;
+}
+
+WebInspector.DOMPresentationUtils.linkifyNodeById = function(nodeId)
+{
+    var node = WebInspector.domAgent.nodeForId(nodeId);
+    if (!node)
+        return document.createTextNode(WebInspector.UIString("<node>"));
+    return WebInspector.DOMPresentationUtils.linkifyNodeReference(node);
+}
+
+/**
+ * @param {string} imageURL
+ * @param {boolean} showDimensions
+ * @param {function(Element=)} userCallback
+ * @param {Object=} precomputedDimensions
+ */
+WebInspector.DOMPresentationUtils.buildImagePreviewContents = function(imageURL, showDimensions, userCallback, precomputedDimensions)
+{
+    var resource = WebInspector.resourceTreeModel.resourceForURL(imageURL);
+    if (!resource) {
+        userCallback();
+        return;
+    }
+
+    var imageElement = document.createElement("img");
+    imageElement.addEventListener("load", buildContent, false);
+    imageElement.addEventListener("error", errorCallback, false);
+    resource.populateImageSource(imageElement);
+
+    function errorCallback()
+    {
+        // Drop the event parameter when invoking userCallback.
+        userCallback();
+    }
+
+    function buildContent()
+    {
+        var container = document.createElement("table");
+        container.className = "image-preview-container";
+        var naturalWidth = precomputedDimensions ? precomputedDimensions.naturalWidth : imageElement.naturalWidth;
+        var naturalHeight = precomputedDimensions ? precomputedDimensions.naturalHeight : imageElement.naturalHeight;
+        var offsetWidth = precomputedDimensions ? precomputedDimensions.offsetWidth : naturalWidth;
+        var offsetHeight = precomputedDimensions ? precomputedDimensions.offsetHeight : naturalHeight;
+        var description;
+        if (showDimensions) {
+            if (offsetHeight === naturalHeight && offsetWidth === naturalWidth)
+                description = WebInspector.UIString("%d \xd7 %d pixels", offsetWidth, offsetHeight);
+            else
+                description = WebInspector.UIString("%d \xd7 %d pixels (Natural: %d \xd7 %d pixels)", offsetWidth, offsetHeight, naturalWidth, naturalHeight);
+        }
+
+        container.createChild("tr").createChild("td", "image-container").appendChild(imageElement);
+        if (description)
+            container.createChild("tr").createChild("td").createChild("span", "description").textContent = description;
+        userCallback(container);
+    }
+}
diff --git a/Source/devtools/front_end/DOMStorage.js b/Source/devtools/front_end/DOMStorage.js
new file mode 100644
index 0000000..b209f72
--- /dev/null
+++ b/Source/devtools/front_end/DOMStorage.js
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2008 Nokia Inc.  All rights reserved.
+ * Copyright (C) 2013 Samsung Electronics. 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.
+ * 3.  Neither the name of Apple Computer, 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 "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.
+ */
+
+/**
+ * @constructor
+ * @param {string} securityOrigin
+ * @param {boolean} isLocalStorage
+ */
+WebInspector.DOMStorage = function(securityOrigin, isLocalStorage)
+{
+    this._securityOrigin = securityOrigin;
+    this._isLocalStorage = isLocalStorage;
+}
+
+/**
+ * @param {string} securityOrigin
+ * @param {boolean} isLocalStorage
+ * @return {DOMStorageAgent.StorageId}
+ */
+WebInspector.DOMStorage.storageId = function(securityOrigin, isLocalStorage)
+{
+    return { securityOrigin: securityOrigin, isLocalStorage: isLocalStorage };
+}
+
+WebInspector.DOMStorage.prototype = {
+
+    /** @return {DOMStorageAgent.StorageId} */
+    get id()
+    {
+        return WebInspector.DOMStorage.storageId(this._securityOrigin, this._isLocalStorage);
+    },
+
+    /** @return {string} */
+    get securityOrigin()
+    {
+        return this._securityOrigin;
+    },
+
+    /** @return {boolean} */
+    get isLocalStorage()
+    {
+        return this._isLocalStorage;
+    },
+
+    /**
+     * @param {function(?Protocol.Error, Array.<DOMStorageAgent.Item>):void=} callback
+     */
+    getItems: function(callback)
+    {
+        DOMStorageAgent.getDOMStorageItems(this.id, callback);
+    },
+
+    /**
+     * @param {string} key
+     * @param {string} value
+     * @param {function(?Protocol.Error):void=} callback
+     */
+    setItem: function(key, value, callback)
+    {
+        DOMStorageAgent.setDOMStorageItem(this.id, key, value, callback);
+    },
+
+    /**
+     * @param {string} key
+     * @param {function(?Protocol.Error):void=} callback
+     */
+    removeItem: function(key, callback)
+    {
+        DOMStorageAgent.removeDOMStorageItem(this.id, key, callback);
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.DOMStorageModel = function()
+{
+    this._storages = {};
+    InspectorBackend.registerDOMStorageDispatcher(new WebInspector.DOMStorageDispatcher(this));
+    DOMStorageAgent.enable();
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginAdded, this._securityOriginAdded, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginRemoved, this._securityOriginRemoved, this);
+}
+
+WebInspector.DOMStorageModel.Events = {
+    DOMStorageAdded: "DOMStorageAdded",
+    DOMStorageRemoved: "DOMStorageRemoved",
+    DOMStorageItemsCleared: "DOMStorageItemsCleared",
+    DOMStorageItemRemoved: "DOMStorageItemRemoved",
+    DOMStorageItemAdded: "DOMStorageItemAdded",
+    DOMStorageItemUpdated: "DOMStorageItemUpdated"
+}
+
+WebInspector.DOMStorageModel.prototype = {
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _securityOriginAdded: function(event)
+    {
+        var securityOrigin = /** @type {string} */ (event.data);
+        var localStorageKey = this._storageKey(securityOrigin, true);
+        console.assert(!this._storages[localStorageKey]);
+        var localStorage = new WebInspector.DOMStorage(securityOrigin, true);
+        this._storages[localStorageKey] = localStorage;
+        this.dispatchEventToListeners(WebInspector.DOMStorageModel.Events.DOMStorageAdded, localStorage);
+
+        var sessionStorageKey = this._storageKey(securityOrigin, false);
+        console.assert(!this._storages[sessionStorageKey]);
+        var sessionStorage = new WebInspector.DOMStorage(securityOrigin, false);
+        this._storages[sessionStorageKey] = sessionStorage;
+        this.dispatchEventToListeners(WebInspector.DOMStorageModel.Events.DOMStorageAdded, sessionStorage);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _securityOriginRemoved: function(event)
+    {
+        var securityOrigin = /** @type {string} */ (event.data);
+        var localStorageKey = this._storageKey(securityOrigin, true);
+        var localStorage = this._storages[localStorageKey];
+        console.assert(localStorage);
+        delete this._storages[localStorageKey];
+        this.dispatchEventToListeners(WebInspector.DOMStorageModel.Events.DOMStorageRemoved, localStorage);
+
+        var sessionStorageKey = this._storageKey(securityOrigin, false);
+        var sessionStorage = this._storages[sessionStorageKey];
+        console.assert(sessionStorage);
+        delete this._storages[sessionStorageKey];
+        this.dispatchEventToListeners(WebInspector.DOMStorageModel.Events.DOMStorageRemoved, sessionStorage);
+    },
+
+    /**
+     * @param {string} securityOrigin
+     * @param {boolean} isLocalStorage
+     * @return {string}
+     */
+    _storageKey: function(securityOrigin, isLocalStorage)
+    {
+        return JSON.stringify(WebInspector.DOMStorage.storageId(securityOrigin, isLocalStorage));
+    },
+
+    /**
+     * @param {DOMStorageAgent.StorageId} storageId
+     */
+    _domStorageItemsCleared: function(storageId)
+    {
+        var domStorage = this.storageForId(storageId);
+        var storageData = {
+            storage: domStorage
+        };
+        this.dispatchEventToListeners(WebInspector.DOMStorageModel.Events.DOMStorageItemsCleared, storageData);
+    },
+
+    /**
+     * @param {DOMStorageAgent.StorageId} storageId
+     * @param {string} key
+     */
+    _domStorageItemRemoved: function(storageId, key)
+    {
+        var domStorage = this.storageForId(storageId);
+        var storageData = {
+            storage: domStorage,
+            key: key
+        };
+        this.dispatchEventToListeners(WebInspector.DOMStorageModel.Events.DOMStorageItemRemoved, storageData);
+    },
+
+    /**
+     * @param {DOMStorageAgent.StorageId} storageId
+     * @param {string} key
+     * @param {string} newValue
+     */
+    _domStorageItemAdded: function(storageId, key, newValue)
+    {
+        var domStorage = this.storageForId(storageId);
+        var storageData = {
+            storage: domStorage,
+            key: key,
+            newValue: newValue
+        };
+        this.dispatchEventToListeners(WebInspector.DOMStorageModel.Events.DOMStorageItemAdded, storageData);
+    },
+
+    /**
+     * @param {DOMStorageAgent.StorageId} storageId
+     * @param {string} key
+     * @param {string} oldValue
+     * @param {string} newValue
+     */
+    _domStorageItemUpdated: function(storageId, key, oldValue, newValue)
+    {
+        var domStorage = this._storages[storageId];
+        var storageData = {
+            storage: domStorage,
+            key: key,
+            oldValue: oldValue,
+            newValue: newValue
+        };
+        this.dispatchEventToListeners(WebInspector.DOMStorageModel.Events.DOMStorageItemUpdated, storageData);
+    },
+
+    /**
+     * @param {DOMStorageAgent.StorageId} storageId
+     * @return {WebInspector.DOMStorage}
+     */
+    storageForId: function(storageId)
+    {
+        return this._storages[JSON.stringify(storageId)];
+    },
+
+    /**
+     * @return {Array.<WebInspector.DOMStorage>}
+     */
+    storages: function()
+    {
+        var result = [];
+        for (var id in this._storages)
+            result.push(this._storages[id]);
+        return result;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {DOMStorageAgent.Dispatcher}
+ * @param {WebInspector.DOMStorageModel} model
+ */
+WebInspector.DOMStorageDispatcher = function(model)
+{
+    this._model = model;
+}
+
+WebInspector.DOMStorageDispatcher.prototype = {
+
+    /**
+     * @param {DOMStorageAgent.StorageId} storageId
+     */
+    domStorageItemsCleared: function(storageId)
+    {
+        this._model._domStorageItemsCleared(storageId);
+    },
+
+    /**
+     * @param {DOMStorageAgent.StorageId} storageId
+     * @param {string} key
+     */
+    domStorageItemRemoved: function(storageId, key)
+    {
+        this._model._domStorageItemRemoved(storageId, key);
+    },
+
+    /**
+     * @param {DOMStorageAgent.StorageId} storageId
+     * @param {string} key
+     * @param {string} newValue
+     */
+    domStorageItemAdded: function(storageId, key, newValue)
+    {
+        this._model._domStorageItemAdded(storageId, key, newValue);
+    },
+
+    /**
+     * @param {DOMStorageAgent.StorageId} storageId
+     * @param {string} key
+     * @param {string} oldValue
+     * @param {string} newValue
+     */
+    domStorageItemUpdated: function(storageId, key, oldValue, newValue)
+    {
+        this._model._domStorageItemUpdated(storageId, key, oldValue, newValue);
+    },
+}
+
+/**
+ * @type {WebInspector.DOMStorageModel}
+ */
+WebInspector.domStorageModel = null;
diff --git a/Source/devtools/front_end/DOMStorageItemsView.js b/Source/devtools/front_end/DOMStorageItemsView.js
new file mode 100644
index 0000000..dd9f545
--- /dev/null
+++ b/Source/devtools/front_end/DOMStorageItemsView.js
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2008 Nokia Inc.  All rights reserved.
+ * Copyright (C) 2013 Samsung Electronics. 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 ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.DOMStorageItemsView = function(domStorage, domStorageModel)
+{
+    WebInspector.View.call(this);
+
+    this.domStorage = domStorage;
+    this.domStorageModel = domStorageModel;
+
+    this.element.addStyleClass("storage-view");
+    this.element.addStyleClass("table");
+
+    this.deleteButton = new WebInspector.StatusBarButton(WebInspector.UIString("Delete"), "delete-storage-status-bar-item");
+    this.deleteButton.visible = false;
+    this.deleteButton.addEventListener("click", this._deleteButtonClicked, this);
+
+    this.refreshButton = new WebInspector.StatusBarButton(WebInspector.UIString("Refresh"), "refresh-storage-status-bar-item");
+    this.refreshButton.addEventListener("click", this._refreshButtonClicked, this);
+
+    this.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageItemsCleared, this._domStorageItemsCleared, this);
+    this.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageItemRemoved, this._domStorageItemRemoved, this);
+    this.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageItemAdded, this._domStorageItemAdded, this);
+    this.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageItemUpdated, this._domStorageItemUpdated, this);
+}
+
+WebInspector.DOMStorageItemsView.prototype = {
+    get statusBarItems()
+    {
+        return [this.refreshButton.element, this.deleteButton.element];
+    },
+
+    wasShown: function()
+    {
+        this._update();
+    },
+
+    willHide: function()
+    {
+        this.deleteButton.visible = false;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _domStorageItemsCleared: function(event)
+    {
+        if (!this.isShowing() || !this._dataGrid)
+            return;
+
+        this._dataGrid.rootNode().removeChildren();
+        this._dataGrid.addCreationNode(false);
+        this.deleteButton.visible = false;
+        event.consume(true);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _domStorageItemRemoved: function(event)
+    {
+        if (!this.isShowing() || !this._dataGrid)
+            return;
+
+        var storageData = event.data;
+        var rootNode = this._dataGrid.rootNode();
+        var children = rootNode.children;
+
+        event.consume(true);
+
+        for (var i = 0; i < children.length; ++i) {
+            var childNode = children[i];
+            if (childNode.data.key === storageData.key) {
+                rootNode.removeChild(childNode);
+                this.deleteButton.visible = (children.length > 1);
+                return;
+            }
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _domStorageItemAdded: function(event)
+    {
+        if (!this.isShowing() || !this._dataGrid)
+            return;
+
+        var storageData = event.data;
+        var rootNode = this._dataGrid.rootNode();
+        var children = rootNode.children;
+
+        event.consume(true);
+        this.deleteButton.visible = true;
+
+        for (var i = 0; i < children.length; ++i)
+            if (children[i].data.key === storageData.key)
+                return;
+
+        var childNode = new WebInspector.DataGridNode({key: storageData.key, value: storageData.newValue}, false);
+        rootNode.insertChild(childNode, children.length - 1);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _domStorageItemUpdated: function(event)
+    {
+        if (!this.isShowing() || !this._dataGrid)
+            return;
+
+        var storageData = event.data;
+        var rootNode = this._dataGrid.rootNode();
+        var children = rootNode.children;
+
+        event.consume(true);
+
+        var keyFound = false;
+        for (var i = 0; i < children.length; ++i) {
+            var childNode = children[i];
+            if (childNode.data.key === storageData.key) {
+                if (keyFound) {
+                    rootNode.removeChild(childNode);
+                    return;
+                }
+                keyFound = true;
+                if (childNode.data.value !== storageData.newValue) {
+                    childNode.data.value = storageData.newValue;
+                    childNode.refresh();
+                    childNode.select();
+                    childNode.reveal();
+                }
+                this.deleteButton.visible = true;
+            }
+        }
+    },
+
+    _update: function()
+    {
+        this.detachChildViews();
+        this.domStorage.getItems(this._showDOMStorageItems.bind(this));
+    },
+
+    _showDOMStorageItems: function(error, items)
+    {
+        if (error)
+            return;
+
+        this._dataGrid = this._dataGridForDOMStorageItems(items);
+        this._dataGrid.show(this.element);
+        this.deleteButton.visible = (this._dataGrid.rootNode().children.length > 1);
+    },
+
+    _dataGridForDOMStorageItems: function(items)
+    {
+        var columns = [
+            {id: "key", title: WebInspector.UIString("Key"), editable: true, weight: 50},
+            {id: "value", title: WebInspector.UIString("Value"), editable: true, weight: 50}
+        ];
+
+        var nodes = [];
+
+        var keys = [];
+        var length = items.length;
+        for (var i = 0; i < items.length; i++) {
+            var key = items[i][0];
+            var value = items[i][1];
+            var node = new WebInspector.DataGridNode({key: key, value: value}, false);
+            node.selectable = true;
+            nodes.push(node);
+            keys.push(key);
+        }
+
+        var dataGrid = new WebInspector.DataGrid(columns, this._editingCallback.bind(this), this._deleteCallback.bind(this));
+        dataGrid.setName("DOMStorageItemsView");
+        length = nodes.length;
+        for (var i = 0; i < length; ++i)
+            dataGrid.rootNode().appendChild(nodes[i]);
+        dataGrid.addCreationNode(false);
+        if (length > 0)
+            nodes[0].selected = true;
+        return dataGrid;
+    },
+
+    _deleteButtonClicked: function(event)
+    {
+        if (!this._dataGrid || !this._dataGrid.selectedNode)
+            return;
+
+        this._deleteCallback(this._dataGrid.selectedNode);
+        this._dataGrid.changeNodeAfterDeletion();
+    },
+
+    _refreshButtonClicked: function(event)
+    {
+        this._update();
+    },
+
+    _editingCallback: function(editingNode, columnIdentifier, oldText, newText)
+    {
+        var domStorage = this.domStorage;
+        if ("key" === columnIdentifier) {
+            if (oldText)
+                domStorage.removeItem(oldText);
+            domStorage.setItem(newText, editingNode.data.value);
+            this._removeDupes(editingNode);
+        } else
+            domStorage.setItem(editingNode.data.key, newText);
+    },
+
+    /**
+     * @param {!WebInspector.DataGridNode} masterNode
+     */
+    _removeDupes: function(masterNode)
+    {
+        var rootNode = this._dataGrid.rootNode();
+        var children = rootNode.children;
+        for (var i = children.length - 1; i >= 0; --i) {
+            var childNode = children[i];
+            if ((childNode.data.key === masterNode.data.key) && (masterNode !== childNode))
+                rootNode.removeChild(childNode);
+        }
+    },
+
+    _deleteCallback: function(node)
+    {
+        if (!node || node.isCreationNode)
+            return;
+
+        if (this.domStorage)
+            this.domStorage.removeItem(node.data.key);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/DOMSyntaxHighlighter.js b/Source/devtools/front_end/DOMSyntaxHighlighter.js
new file mode 100644
index 0000000..4ae97eb
--- /dev/null
+++ b/Source/devtools/front_end/DOMSyntaxHighlighter.js
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.DOMSyntaxHighlighter = function(mimeType, stripExtraWhitespace)
+{
+    this._tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer(mimeType);
+    this._stripExtraWhitespace = stripExtraWhitespace;
+}
+
+WebInspector.DOMSyntaxHighlighter.prototype = {
+    createSpan: function(content, className)
+    {
+        var span = document.createElement("span");
+        span.className = "webkit-" + className;
+        if (this._stripExtraWhitespace && className !== "whitespace")
+            content = content.replace(/^[\n\r]*/, "").replace(/\s*$/, "");
+        span.appendChild(document.createTextNode(content));
+        return span;
+    },
+
+    syntaxHighlightNode: function(node)
+    {
+        this._tokenizer.condition = this._tokenizer.createInitialCondition();
+        var lines = node.textContent.split("\n");
+        node.removeChildren();
+
+        for (var i = lines[0].length ? 0 : 1; i < lines.length; ++i) {
+            var line = lines[i];
+            var plainTextStart = 0;
+            this._tokenizer.line = line;
+            var column = 0;
+            do {
+                var newColumn = this._tokenizer.nextToken(column);
+                var tokenType = this._tokenizer.tokenType;
+                if (tokenType) {
+                    if (column > plainTextStart) {
+                        var plainText = line.substring(plainTextStart, column);
+                        node.appendChild(document.createTextNode(plainText));
+                    }
+                    var token = line.substring(column, newColumn);
+                    node.appendChild(this.createSpan(token, tokenType));
+                    plainTextStart = newColumn;
+                }
+                column = newColumn;
+           } while (column < line.length)
+
+           if (plainTextStart < line.length) {
+               var plainText = line.substring(plainTextStart, line.length);
+               node.appendChild(document.createTextNode(plainText));
+           }
+           if (i < lines.length - 1)
+               node.appendChild(document.createElement("br"));
+        }
+    }
+}
diff --git a/Source/devtools/front_end/DataGrid.js b/Source/devtools/front_end/DataGrid.js
new file mode 100644
index 0000000..539c642
--- /dev/null
+++ b/Source/devtools/front_end/DataGrid.js
@@ -0,0 +1,1793 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columnsArray
+ * @param {function(WebInspector.DataGridNode, string, string, string)=} editCallback
+ * @param {function(WebInspector.DataGridNode)=} deleteCallback
+ * @param {function()=} refreshCallback
+ * @param {function(!WebInspector.ContextMenu, WebInspector.DataGridNode)=} contextMenuCallback
+ */
+WebInspector.DataGrid = function(columnsArray, editCallback, deleteCallback, refreshCallback, contextMenuCallback)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("dataGrid.css");
+
+    this.element.className = "data-grid";
+    this.element.tabIndex = 0;
+    this.element.addEventListener("keydown", this._keyDown.bind(this), false);
+
+    this._headerTable = document.createElement("table");
+    this._headerTable.className = "header";
+    this._headerTableHeaders = {};
+
+    this._dataTable = document.createElement("table");
+    this._dataTable.className = "data";
+
+    this._dataTable.addEventListener("mousedown", this._mouseDownInDataTable.bind(this), true);
+    this._dataTable.addEventListener("click", this._clickInDataTable.bind(this), true);
+
+    this._dataTable.addEventListener("contextmenu", this._contextMenuInDataTable.bind(this), true);
+
+    // FIXME: Add a createCallback which is different from editCallback and has different
+    // behavior when creating a new node.
+    if (editCallback)
+        this._dataTable.addEventListener("dblclick", this._ondblclick.bind(this), false);
+    this._editCallback = editCallback;
+    this._deleteCallback = deleteCallback;
+    this._refreshCallback = refreshCallback;
+    this._contextMenuCallback = contextMenuCallback;
+
+    this._scrollContainer = document.createElement("div");
+    this._scrollContainer.className = "data-container";
+    this._scrollContainer.appendChild(this._dataTable);
+
+    this.element.appendChild(this._headerTable);
+    this.element.appendChild(this._scrollContainer);
+
+    var headerRow = document.createElement("tr");
+    var columnGroup = document.createElement("colgroup");
+    columnGroup.span = columnsArray.length;
+
+    var fillerRow = document.createElement("tr");
+    fillerRow.className = "filler";
+
+    this._columnsArray = columnsArray;
+    this.columns = {};
+
+    for (var i = 0; i < columnsArray.length; ++i) {
+        var column = columnsArray[i];
+        column.ordinal = i;
+        var columnIdentifier = column.identifier = column.id || i;
+        this.columns[columnIdentifier] = column;
+        if (column.disclosure)
+            this.disclosureColumnIdentifier = columnIdentifier;
+
+        var col = document.createElement("col");
+        if (column.width)
+            col.style.width = column.width;
+        column.element = col;
+        columnGroup.appendChild(col);
+
+        var cell = document.createElement("th");
+        cell.className = columnIdentifier + "-column";
+        cell.columnIdentifier = columnIdentifier;
+        this._headerTableHeaders[columnIdentifier] = cell;
+
+        var div = document.createElement("div");
+        if (column.titleDOMFragment)
+            div.appendChild(column.titleDOMFragment);
+        else
+            div.textContent = column.title;
+        cell.appendChild(div);
+
+        if (column.sort) {
+            cell.addStyleClass("sort-" + column.sort);
+            this._sortColumnCell = cell;
+        }
+
+        if (column.sortable) {
+            cell.addEventListener("click", this._clickInHeaderCell.bind(this), false);
+            cell.addStyleClass("sortable");
+        }
+
+        headerRow.appendChild(cell);
+        fillerRow.createChild("td", columnIdentifier + "-column");
+    }
+
+    headerRow.createChild("th", "corner");
+    fillerRow.createChild("td", "corner");
+    columnGroup.createChild("col", "corner");
+
+    this._headerTableColumnGroup = columnGroup;
+    this._headerTable.appendChild(this._headerTableColumnGroup);
+    this.headerTableBody.appendChild(headerRow);
+
+    this._dataTableColumnGroup = columnGroup.cloneNode(true);
+    this._dataTable.appendChild(this._dataTableColumnGroup);
+    this.dataTableBody.appendChild(fillerRow);
+
+    this.selectedNode = null;
+    this.expandNodesWhenArrowing = false;
+    this.setRootNode(new WebInspector.DataGridNode());
+    this.indentWidth = 15;
+    this.resizers = [];
+    this._columnWidthsInitialized = false;
+}
+
+/** @typedef {{id: ?string, editable: boolean, longText: ?boolean, sort: WebInspector.DataGrid.Order, sortable: boolean, align: WebInspector.DataGrid.Align}} */
+WebInspector.DataGrid.ColumnDescriptor;
+
+WebInspector.DataGrid.Events = {
+    SelectedNode: "SelectedNode",
+    DeselectedNode: "DeselectedNode",
+    SortingChanged: "SortingChanged",
+    ColumnsResized: "ColumnsResized"
+}
+
+/** @enum {string} */
+WebInspector.DataGrid.Order = {
+    Ascending: "ascending",
+    Descending: "descending"
+}
+
+/** @enum {string} */
+WebInspector.DataGrid.Align = {
+    Center: "center",
+    Right: "right"
+}
+
+/**
+ * @param {Array.<string>} columnNames
+ * @param {Array.<string>} values
+ */
+WebInspector.DataGrid.createSortableDataGrid = function(columnNames, values)
+{
+    var numColumns = columnNames.length;
+    if (!numColumns)
+        return null;
+
+    var columns = [];
+    for (var i = 0; i < columnNames.length; ++i)
+        columns.push({title: columnNames[i], width: columnNames[i].length, sortable: true});
+
+    var nodes = [];
+    for (var i = 0; i < values.length / numColumns; ++i) {
+        var data = {};
+        for (var j = 0; j < columnNames.length; ++j)
+            data[j] = values[numColumns * i + j];
+
+        var node = new WebInspector.DataGridNode(data, false);
+        node.selectable = false;
+        nodes.push(node);
+    }
+
+    var dataGrid = new WebInspector.DataGrid(columns);
+    var length = nodes.length;
+    for (var i = 0; i < length; ++i)
+        dataGrid.rootNode().appendChild(nodes[i]);
+
+    dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, sortDataGrid, this);
+
+    function sortDataGrid()
+    {
+        var nodes = dataGrid._rootNode.children.slice();
+        var sortColumnIdentifier = dataGrid.sortColumnIdentifier();
+        var sortDirection = dataGrid.isSortOrderAscending() ? 1 : -1;
+        var columnIsNumeric = true;
+
+        for (var i = 0; i < nodes.length; i++) {
+            if (isNaN(Number(nodes[i].data[sortColumnIdentifier])))
+                columnIsNumeric = false;
+        }
+
+        function comparator(dataGridNode1, dataGridNode2)
+        {
+            var item1 = dataGridNode1.data[sortColumnIdentifier];
+            var item2 = dataGridNode2.data[sortColumnIdentifier];
+            item1 = item1 instanceof Node ? item1.textContent : String(item1);
+            item2 = item2 instanceof Node ? item2.textContent : String(item2);
+
+            var comparison;
+            if (columnIsNumeric) {
+                // Sort numbers based on comparing their values rather than a lexicographical comparison.
+                var number1 = parseFloat(item1);
+                var number2 = parseFloat(item2);
+                comparison = number1 < number2 ? -1 : (number1 > number2 ? 1 : 0);
+            } else
+                comparison = item1 < item2 ? -1 : (item1 > item2 ? 1 : 0);
+
+            return sortDirection * comparison;
+        }
+
+        nodes.sort(comparator);
+        dataGrid.rootNode().removeChildren();
+        for (var i = 0; i < nodes.length; i++)
+            dataGrid._rootNode.appendChild(nodes[i]);
+    }
+    return dataGrid;
+}
+
+WebInspector.DataGrid.prototype = {
+    /**
+     * @param {!WebInspector.DataGridNode} rootNode
+     */
+    setRootNode: function(rootNode)
+    {
+        if (this._rootNode) {
+            this._rootNode.removeChildren();
+            this._rootNode.dataGrid = null;
+            this._rootNode._isRoot = false;
+        }
+        /** @type {!WebInspector.DataGridNode} */
+        this._rootNode = rootNode;
+        rootNode._isRoot = true;
+        rootNode.hasChildren = false;
+        rootNode._expanded = true;
+        rootNode._revealed = true;
+        rootNode.dataGrid = this;
+    },
+
+    /**
+     * @return {!WebInspector.DataGridNode}
+     */
+    rootNode: function()
+    {
+        return this._rootNode;
+    },
+
+    _ondblclick: function(event)
+    {
+        if (this._editing || this._editingNode)
+            return;
+
+        var columnIdentifier = this.columnIdentifierFromNode(event.target);
+        if (!columnIdentifier || !this.columns[columnIdentifier].editable)
+            return;
+        this._startEditing(event.target);
+    },
+
+    /**
+     * @param {!WebInspector.DataGridNode} node
+     * @param {number} columnOrdinal
+     */
+    _startEditingColumnOfDataGridNode: function(node, columnOrdinal)
+    {
+        this._editing = true;
+        /** @type {WebInspector.DataGridNode} */
+        this._editingNode = node;
+        this._editingNode.select();
+
+        var element = this._editingNode._element.children[columnOrdinal];
+        WebInspector.startEditing(element, this._startEditingConfig(element));
+        window.getSelection().setBaseAndExtent(element, 0, element, 1);
+    },
+
+    _startEditing: function(target)
+    {
+        var element = target.enclosingNodeOrSelfWithNodeName("td");
+        if (!element)
+            return;
+
+        this._editingNode = this.dataGridNodeFromNode(target);
+        if (!this._editingNode) {
+            if (!this.creationNode)
+                return;
+            this._editingNode = this.creationNode;
+        }
+
+        // Force editing the 1st column when editing the creation node
+        if (this._editingNode.isCreationNode)
+            return this._startEditingColumnOfDataGridNode(this._editingNode, this._nextEditableColumn(-1));
+
+        this._editing = true;
+        WebInspector.startEditing(element, this._startEditingConfig(element));
+
+        window.getSelection().setBaseAndExtent(element, 0, element, 1);
+    },
+
+    renderInline: function()
+    {
+        this.element.addStyleClass("inline");
+    },
+
+    _startEditingConfig: function(element)
+    {
+        return new WebInspector.EditingConfig(this._editingCommitted.bind(this), this._editingCancelled.bind(this), element.textContent);
+    },
+
+    _editingCommitted: function(element, newText, oldText, context, moveDirection)
+    {
+        var columnIdentifier = this.columnIdentifierFromNode(element);
+        if (!columnIdentifier) {
+            this._editingCancelled(element);
+            return;
+        }
+        var columnOrdinal = this.columns[columnIdentifier].ordinal;
+        var textBeforeEditing = this._editingNode.data[columnIdentifier];
+        var currentEditingNode = this._editingNode;
+
+        function moveToNextIfNeeded(wasChange) {
+            if (!moveDirection)
+                return;
+
+            if (moveDirection === "forward") {
+            var firstEditableColumn = this._nextEditableColumn(-1);
+                if (currentEditingNode.isCreationNode && columnOrdinal === firstEditableColumn && !wasChange)
+                    return;
+
+                var nextEditableColumn = this._nextEditableColumn(columnOrdinal);
+                if (nextEditableColumn !== -1)
+                    return this._startEditingColumnOfDataGridNode(currentEditingNode, nextEditableColumn);
+
+                var nextDataGridNode = currentEditingNode.traverseNextNode(true, null, true);
+                if (nextDataGridNode)
+                    return this._startEditingColumnOfDataGridNode(nextDataGridNode, firstEditableColumn);
+                if (currentEditingNode.isCreationNode && wasChange) {
+                    this.addCreationNode(false);
+                    return this._startEditingColumnOfDataGridNode(this.creationNode, firstEditableColumn);
+                }
+                return;
+            }
+
+            if (moveDirection === "backward") {
+                var prevEditableColumn = this._nextEditableColumn(columnOrdinal, true);
+                if (prevEditableColumn !== -1)
+                    return this._startEditingColumnOfDataGridNode(currentEditingNode, prevEditableColumn);
+
+                var lastEditableColumn = this._nextEditableColumn(this._columnsArray.length, true);
+                var nextDataGridNode = currentEditingNode.traversePreviousNode(true, true);
+                if (nextDataGridNode)
+                    return this._startEditingColumnOfDataGridNode(nextDataGridNode, lastEditableColumn);
+                return;
+            }
+        }
+
+        if (textBeforeEditing == newText) {
+            this._editingCancelled(element);
+            moveToNextIfNeeded.call(this, false);
+            return;
+        }
+
+        // Update the text in the datagrid that we typed
+        this._editingNode.data[columnIdentifier] = newText;
+
+        // Make the callback - expects an editing node (table row), the column number that is being edited,
+        // the text that used to be there, and the new text.
+        this._editCallback(this._editingNode, columnIdentifier, textBeforeEditing, newText);
+
+        if (this._editingNode.isCreationNode)
+            this.addCreationNode(false);
+
+        this._editingCancelled(element);
+        moveToNextIfNeeded.call(this, true);
+    },
+
+    _editingCancelled: function(element)
+    {
+        delete this._editing;
+        this._editingNode = null;
+    },
+
+    /**
+     * @param {number} columnOrdinal
+     * @param {boolean=} moveBackward
+     * @return {number}
+     */
+    _nextEditableColumn: function(columnOrdinal, moveBackward)
+    {
+        var increment = moveBackward ? -1 : 1;
+        var columns = this._columnsArray;
+        for (var i = columnOrdinal + increment; (i >= 0) && (i < columns.length); i += increment) {
+            if (columns[i].editable)
+                return i;
+        }
+        return -1;
+    },
+
+    /**
+     * @return {?string}
+     */
+    sortColumnIdentifier: function()
+    {
+        if (!this._sortColumnCell)
+            return null;
+        return this._sortColumnCell.columnIdentifier;
+    },
+
+    /**
+     * @return {?string}
+     */
+    sortOrder: function()
+    {
+        if (!this._sortColumnCell || this._sortColumnCell.hasStyleClass("sort-ascending"))
+            return WebInspector.DataGrid.Order.Ascending;
+        if (this._sortColumnCell.hasStyleClass("sort-descending"))
+            return WebInspector.DataGrid.Order.Descending;
+        return null;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isSortOrderAscending: function()
+    {
+        return !this._sortColumnCell || this._sortColumnCell.hasStyleClass("sort-ascending");
+    },
+
+    get headerTableBody()
+    {
+        if ("_headerTableBody" in this)
+            return this._headerTableBody;
+
+        this._headerTableBody = this._headerTable.getElementsByTagName("tbody")[0];
+        if (!this._headerTableBody) {
+            this._headerTableBody = this.element.ownerDocument.createElement("tbody");
+            this._headerTable.insertBefore(this._headerTableBody, this._headerTable.tFoot);
+        }
+
+        return this._headerTableBody;
+    },
+
+    get dataTableBody()
+    {
+        if ("_dataTableBody" in this)
+            return this._dataTableBody;
+
+        this._dataTableBody = this._dataTable.getElementsByTagName("tbody")[0];
+        if (!this._dataTableBody) {
+            this._dataTableBody = this.element.ownerDocument.createElement("tbody");
+            this._dataTable.insertBefore(this._dataTableBody, this._dataTable.tFoot);
+        }
+
+        return this._dataTableBody;
+    },
+
+    /**
+     * @param {Array.<number>} widths
+     * @param {number} minPercent
+     * @param {number=} maxPercent
+     */
+    _autoSizeWidths: function(widths, minPercent, maxPercent)
+    {
+        if (minPercent)
+            minPercent = Math.min(minPercent, Math.floor(100 / widths.length));
+        var totalWidth = 0;
+        for (var i = 0; i < widths.length; ++i)
+            totalWidth += widths[i];
+        var totalPercentWidth = 0;
+        for (var i = 0; i < widths.length; ++i) {
+            var width = Math.round(100 * widths[i] / totalWidth);
+            if (minPercent && width < minPercent)
+                width = minPercent;
+            else if (maxPercent && width > maxPercent)
+                width = maxPercent;
+            totalPercentWidth += width;
+            widths[i] = width;
+        }
+        var recoupPercent = totalPercentWidth - 100;
+
+        while (minPercent && recoupPercent > 0) {
+            for (var i = 0; i < widths.length; ++i) {
+                if (widths[i] > minPercent) {
+                    --widths[i];
+                    --recoupPercent;
+                    if (!recoupPercent)
+                        break;
+                }
+            }
+        }
+
+        while (maxPercent && recoupPercent < 0) {
+            for (var i = 0; i < widths.length; ++i) {
+                if (widths[i] < maxPercent) {
+                    ++widths[i];
+                    ++recoupPercent;
+                    if (!recoupPercent)
+                        break;
+                }
+            }
+        }
+
+        return widths;
+    },
+
+    /**
+     * @param {number} minPercent
+     * @param {number=} maxPercent
+     * @param {number=} maxDescentLevel
+     */
+    autoSizeColumns: function(minPercent, maxPercent, maxDescentLevel)
+    {
+        var widths = [];
+        for (var i = 0; i < this._columnsArray.length; ++i)
+            widths.push((this._columnsArray[i].title || "").length);
+
+        maxDescentLevel = maxDescentLevel || 0;
+        var children = this._enumerateChildren(this._rootNode, [], maxDescentLevel + 1);
+        for (var i = 0; i < children.length; ++i) {
+            var node = children[i];
+            for (var j = 0; j < this._columnsArray.length; ++j) {
+                var text = node.data[this._columnsArray[j].identifier] || "";
+                if (text.length > widths[j])
+                    widths[j] = text.length;
+            }
+        }
+
+        widths = this._autoSizeWidths(widths, minPercent, maxPercent);
+
+        for (var i = 0; i < this._columnsArray.length; ++i)
+            this._columnsArray[i].element.style.width = widths[i] + "%";
+        this._columnWidthsInitialized = false;
+        this.updateWidths();
+    },
+
+    _enumerateChildren: function(rootNode, result, maxLevel)
+    {
+        if (!rootNode._isRoot)
+            result.push(rootNode);
+        if (!maxLevel)
+            return;
+        for (var i = 0; i < rootNode.children.length; ++i)
+            this._enumerateChildren(rootNode.children[i], result, maxLevel - 1);
+        return result;
+    },
+
+    onResize: function()
+    {
+        this.updateWidths();
+    },
+
+    // Updates the widths of the table, including the positions of the column
+    // resizers.
+    //
+    // IMPORTANT: This function MUST be called once after the element of the
+    // DataGrid is attached to its parent element and every subsequent time the
+    // width of the parent element is changed in order to make it possible to
+    // resize the columns.
+    //
+    // If this function is not called after the DataGrid is attached to its
+    // parent element, then the DataGrid's columns will not be resizable.
+    updateWidths: function()
+    {
+        var headerTableColumns = this._headerTableColumnGroup.children;
+
+        var tableWidth = this._dataTable.offsetWidth;
+        var numColumns = headerTableColumns.length - 1; // Do not process corner column.
+
+        // Do not attempt to use offsetes if we're not attached to the document tree yet.
+        if (!this._columnWidthsInitialized && this.element.offsetWidth) {
+            // Give all the columns initial widths now so that during a resize,
+            // when the two columns that get resized get a percent value for
+            // their widths, all the other columns already have percent values
+            // for their widths.
+            for (var i = 0; i < numColumns; i++) {
+                var columnWidth = this.headerTableBody.rows[0].cells[i].offsetWidth;
+                var percentWidth = ((columnWidth / tableWidth) * 100) + "%";
+                this._headerTableColumnGroup.children[i].style.width = percentWidth;
+                this._dataTableColumnGroup.children[i].style.width = percentWidth;
+            }
+            this._columnWidthsInitialized = true;
+        }
+        this._positionResizers();
+        this.dispatchEventToListeners(WebInspector.DataGrid.Events.ColumnsResized);
+    },
+
+    /**
+     * @param {string} name
+     */
+    setName: function(name)
+    {
+        this._columnWeightsSetting = WebInspector.settings.createSetting("dataGrid-" + name + "-columnWeights", {});
+        this._loadColumnWeights();
+    },
+
+    _loadColumnWeights: function()
+    {
+        if (!this._columnWeightsSetting)
+            return;
+        var weights = this._columnWeightsSetting.get();
+        for (var i = 0; i < this._columnsArray.length; ++i) {
+            var column = this._columnsArray[i];
+            var weight = weights[column.identifier];
+            if (weight)
+                column.weight = weight;
+        }
+        this.applyColumnWeights();
+    },
+
+    _saveColumnWeights: function()
+    {
+        if (!this._columnWeightsSetting)
+            return;
+        var weights = {};
+        for (var i = 0; i < this._columnsArray.length; ++i) {
+            var column = this._columnsArray[i];
+            weights[column.identifier] = column.weight;
+        }
+        this._columnWeightsSetting.set(weights);
+    },
+
+    wasShown: function()
+    {
+       this._loadColumnWeights();
+    },
+
+    applyColumnWeights: function()
+    {
+        var sumOfWeights = 0.0;
+        for (var i = 0; i < this._columnsArray.length; ++i) {
+            var column = this._columnsArray[i];
+            if (this.isColumnVisible(column))
+                sumOfWeights += column.weight;
+        }
+        var factor = 100 / sumOfWeights;
+
+        for (var i = 0; i < this._columnsArray.length; ++i) {
+            var column = this._columnsArray[i];
+            var width = this.isColumnVisible(column) ? ((factor * column.weight) + "%"): "0%";
+            this._headerTableColumnGroup.children[i].style.width = width;
+            this._dataTableColumnGroup.children[i].style.width = width;
+        }
+
+        this._positionResizers();
+        this.dispatchEventToListeners(WebInspector.DataGrid.Events.ColumnsResized);
+    },
+
+    /**
+     * @param {!WebInspector.DataGrid.ColumnDescriptor} column
+     * @return {boolean}
+     */
+    isColumnVisible: function(column)
+    {
+        return !column.hidden;
+    },
+
+    /**
+     * @param {string} columnIdentifier
+     * @param {boolean} visible
+     */
+    setColumnVisible: function(columnIdentifier, visible)
+    {
+        if (visible === !this.columns[columnIdentifier].hidden)
+            return;
+
+        this.columns[columnIdentifier].hidden = !visible;
+        this.element.enableStyleClass("hide-" + columnIdentifier + "-column", !visible);
+    },
+
+    get scrollContainer()
+    {
+        return this._scrollContainer;
+    },
+
+    isScrolledToLastRow: function()
+    {
+        return this._scrollContainer.isScrolledToBottom();
+    },
+
+    scrollToLastRow: function()
+    {
+        this._scrollContainer.scrollTop = this._scrollContainer.scrollHeight - this._scrollContainer.offsetHeight;
+    },
+
+    _positionResizers: function()
+    {
+        var headerTableColumns = this._headerTableColumnGroup.children;
+        var numColumns = headerTableColumns.length - 1; // Do not process corner column.
+        var left = 0;
+        var previousResizer = null;
+
+        // Make n - 1 resizers for n columns.
+        for (var i = 0; i < numColumns - 1; i++) {
+            var resizer = this.resizers[i];
+
+            if (!resizer) {
+                // This is the first call to updateWidth, so the resizers need
+                // to be created.
+                resizer = document.createElement("div");
+                resizer.addStyleClass("data-grid-resizer");
+                // This resizer is associated with the column to its right.
+                WebInspector.installDragHandle(resizer, this._startResizerDragging.bind(this), this._resizerDragging.bind(this), this._endResizerDragging.bind(this), "col-resize");
+                this.element.appendChild(resizer);
+                this.resizers[i] = resizer;
+            }
+
+            // Get the width of the cell in the first (and only) row of the
+            // header table in order to determine the width of the column, since
+            // it is not possible to query a column for its width.
+            left += this.headerTableBody.rows[0].cells[i].offsetWidth;
+
+            if (!this._columnsArray[i].hidden) {
+                resizer.style.removeProperty("display");
+                if (resizer._position !== left) {
+                    resizer._position = left;
+                    resizer.style.left = left + "px";
+                }
+                resizer.leftNeighboringColumnIndex = i;
+                if (previousResizer)
+                    previousResizer.rightNeighboringColumnIndex = i;
+                previousResizer = resizer;
+            } else {
+                if (previousResizer && previousResizer._position !== left) {
+                    previousResizer._position = left;
+                    previousResizer.style.left = left + "px";
+                }
+                resizer.style.setProperty("display", "none");
+                resizer.leftNeighboringColumnIndex = 0;
+                resizer.rightNeighboringColumnIndex = 0;
+            }
+        }
+        if (previousResizer)
+            previousResizer.rightNeighboringColumnIndex = numColumns - 1;
+    },
+
+    addCreationNode: function(hasChildren)
+    {
+        if (this.creationNode)
+            this.creationNode.makeNormal();
+
+        var emptyData = {};
+        for (var column in this.columns)
+            emptyData[column] = '';
+        this.creationNode = new WebInspector.CreationDataGridNode(emptyData, hasChildren);
+        this.rootNode().appendChild(this.creationNode);
+    },
+
+    sortNodes: function(comparator, reverseMode)
+    {
+        function comparatorWrapper(a, b)
+        {
+            if (a._dataGridNode._data.summaryRow)
+                return 1;
+            if (b._dataGridNode._data.summaryRow)
+                return -1;
+
+            var aDataGirdNode = a._dataGridNode;
+            var bDataGirdNode = b._dataGridNode;
+            return reverseMode ? comparator(bDataGirdNode, aDataGirdNode) : comparator(aDataGirdNode, bDataGirdNode);
+        }
+
+        var tbody = this.dataTableBody;
+        var tbodyParent = tbody.parentElement;
+        tbodyParent.removeChild(tbody);
+
+        var childNodes = tbody.childNodes;
+        var fillerRow = childNodes[childNodes.length - 1];
+
+        var sortedRows = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1);
+        sortedRows.sort(comparatorWrapper);
+        var sortedRowsLength = sortedRows.length;
+
+        tbody.removeChildren();
+        var previousSiblingNode = null;
+        for (var i = 0; i < sortedRowsLength; ++i) {
+            var row = sortedRows[i];
+            var node = row._dataGridNode;
+            node.previousSibling = previousSiblingNode;
+            if (previousSiblingNode)
+                previousSiblingNode.nextSibling = node;
+            tbody.appendChild(row);
+            previousSiblingNode = node;
+        }
+        if (previousSiblingNode)
+            previousSiblingNode.nextSibling = null;
+
+        tbody.appendChild(fillerRow);
+        tbodyParent.appendChild(tbody);
+    },
+
+    _keyDown: function(event)
+    {
+        if (!this.selectedNode || event.shiftKey || event.metaKey || event.ctrlKey || this._editing)
+            return;
+
+        var handled = false;
+        var nextSelectedNode;
+        if (event.keyIdentifier === "Up" && !event.altKey) {
+            nextSelectedNode = this.selectedNode.traversePreviousNode(true);
+            while (nextSelectedNode && !nextSelectedNode.selectable)
+                nextSelectedNode = nextSelectedNode.traversePreviousNode(true);
+            handled = nextSelectedNode ? true : false;
+        } else if (event.keyIdentifier === "Down" && !event.altKey) {
+            nextSelectedNode = this.selectedNode.traverseNextNode(true);
+            while (nextSelectedNode && !nextSelectedNode.selectable)
+                nextSelectedNode = nextSelectedNode.traverseNextNode(true);
+            handled = nextSelectedNode ? true : false;
+        } else if (event.keyIdentifier === "Left") {
+            if (this.selectedNode.expanded) {
+                if (event.altKey)
+                    this.selectedNode.collapseRecursively();
+                else
+                    this.selectedNode.collapse();
+                handled = true;
+            } else if (this.selectedNode.parent && !this.selectedNode.parent._isRoot) {
+                handled = true;
+                if (this.selectedNode.parent.selectable) {
+                    nextSelectedNode = this.selectedNode.parent;
+                    handled = nextSelectedNode ? true : false;
+                } else if (this.selectedNode.parent)
+                    this.selectedNode.parent.collapse();
+            }
+        } else if (event.keyIdentifier === "Right") {
+            if (!this.selectedNode.revealed) {
+                this.selectedNode.reveal();
+                handled = true;
+            } else if (this.selectedNode.hasChildren) {
+                handled = true;
+                if (this.selectedNode.expanded) {
+                    nextSelectedNode = this.selectedNode.children[0];
+                    handled = nextSelectedNode ? true : false;
+                } else {
+                    if (event.altKey)
+                        this.selectedNode.expandRecursively();
+                    else
+                        this.selectedNode.expand();
+                }
+            }
+        } else if (event.keyCode === 8 || event.keyCode === 46) {
+            if (this._deleteCallback) {
+                handled = true;
+                this._deleteCallback(this.selectedNode);
+                this.changeNodeAfterDeletion();
+            }
+        } else if (isEnterKey(event)) {
+            if (this._editCallback) {
+                handled = true;
+                this._startEditing(this.selectedNode._element.children[this._nextEditableColumn(-1)]);
+            }
+        }
+
+        if (nextSelectedNode) {
+            nextSelectedNode.reveal();
+            nextSelectedNode.select();
+        }
+
+        if (handled)
+            event.consume(true);
+    },
+
+    changeNodeAfterDeletion: function()
+    {
+        var nextSelectedNode = this.selectedNode.traverseNextNode(true);
+        while (nextSelectedNode && !nextSelectedNode.selectable)
+            nextSelectedNode = nextSelectedNode.traverseNextNode(true);
+
+        if (!nextSelectedNode || nextSelectedNode.isCreationNode) {
+            nextSelectedNode = this.selectedNode.traversePreviousNode(true);
+            while (nextSelectedNode && !nextSelectedNode.selectable)
+                nextSelectedNode = nextSelectedNode.traversePreviousNode(true);
+        }
+
+        if (nextSelectedNode) {
+            nextSelectedNode.reveal();
+            nextSelectedNode.select();
+        }
+    },
+
+    /**
+     * @param {!Node} target
+     * @return {?WebInspector.DataGridNode}
+     */
+    dataGridNodeFromNode: function(target)
+    {
+        var rowElement = target.enclosingNodeOrSelfWithNodeName("tr");
+        return rowElement && rowElement._dataGridNode;
+    },
+
+    /**
+     * @param {!Node} target
+     * @return {?string}
+     */
+    columnIdentifierFromNode: function(target)
+    {
+        var cellElement = target.enclosingNodeOrSelfWithNodeName("td");
+        return cellElement && cellElement.columnIdentifier_;
+    },
+
+    _clickInHeaderCell: function(event)
+    {
+        var cell = event.target.enclosingNodeOrSelfWithNodeName("th");
+        if (!cell || !cell.columnIdentifier || !cell.hasStyleClass("sortable"))
+            return;
+
+        var sortOrder = WebInspector.DataGrid.Order.Ascending;
+        if ((cell === this._sortColumnCell) && this.isSortOrderAscending())
+            sortOrder = WebInspector.DataGrid.Order.Descending;
+
+        if (this._sortColumnCell)
+            this._sortColumnCell.removeMatchingStyleClasses("sort-\\w+");
+        this._sortColumnCell = cell;
+
+        cell.addStyleClass("sort-" + sortOrder);
+
+        this.dispatchEventToListeners(WebInspector.DataGrid.Events.SortingChanged);
+    },
+
+    /**
+     * @param {string} columnIdentifier
+     * @param {!WebInspector.DataGrid.Order} sortOrder
+     */
+    markColumnAsSortedBy: function(columnIdentifier, sortOrder)
+    {
+        if (this._sortColumnCell)
+            this._sortColumnCell.removeMatchingStyleClasses("sort-\\w+");
+        this._sortColumnCell = this._headerTableHeaders[columnIdentifier];
+        this._sortColumnCell.addStyleClass("sort-" + sortOrder);
+    },
+
+    headerTableHeader: function(columnIdentifier)
+    {
+        return this._headerTableHeaders[columnIdentifier];
+    },
+
+    _mouseDownInDataTable: function(event)
+    {
+        var gridNode = this.dataGridNodeFromNode(event.target);
+        if (!gridNode || !gridNode.selectable)
+            return;
+
+        if (gridNode.isEventWithinDisclosureTriangle(event))
+            return;
+
+        if (event.metaKey) {
+            if (gridNode.selected)
+                gridNode.deselect();
+            else
+                gridNode.select();
+        } else
+            gridNode.select();
+    },
+
+    _contextMenuInDataTable: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+
+        var gridNode = this.dataGridNodeFromNode(event.target);
+        if (this._refreshCallback && (!gridNode || gridNode !== this.creationNode))
+            contextMenu.appendItem(WebInspector.UIString("Refresh"), this._refreshCallback.bind(this));
+
+        if (gridNode && gridNode.selectable && !gridNode.isEventWithinDisclosureTriangle(event)) {
+            // FIXME: Use the column names for Editing, instead of just "Edit".
+            if (this._editCallback) {
+                if (gridNode === this.creationNode)
+                    contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add new" : "Add New"), this._startEditing.bind(this, event.target));
+                else {
+                    var columnIdentifier = this.columnIdentifierFromNode(event.target);
+                    if (columnIdentifier && this.columns[columnIdentifier].editable)
+                        contextMenu.appendItem(WebInspector.UIString("Edit"), this._startEditing.bind(this, event.target));
+                }
+            }
+            if (this._deleteCallback && gridNode !== this.creationNode)
+                contextMenu.appendItem(WebInspector.UIString("Delete"), this._deleteCallback.bind(this, gridNode));
+            if (this._contextMenuCallback)
+                this._contextMenuCallback(contextMenu, gridNode);
+        }
+
+        contextMenu.show();
+    },
+
+    _clickInDataTable: function(event)
+    {
+        var gridNode = this.dataGridNodeFromNode(event.target);
+        if (!gridNode || !gridNode.hasChildren)
+            return;
+
+        if (!gridNode.isEventWithinDisclosureTriangle(event))
+            return;
+
+        if (gridNode.expanded) {
+            if (event.altKey)
+                gridNode.collapseRecursively();
+            else
+                gridNode.collapse();
+        } else {
+            if (event.altKey)
+                gridNode.expandRecursively();
+            else
+                gridNode.expand();
+        }
+    },
+
+    get resizeMethod()
+    {
+        if (typeof this._resizeMethod === "undefined")
+            return WebInspector.DataGrid.ResizeMethod.Nearest;
+        return this._resizeMethod;
+    },
+
+    set resizeMethod(method)
+    {
+        this._resizeMethod = method;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _startResizerDragging: function(event)
+    {
+        this._currentResizer = event.target;
+        return !!this._currentResizer.rightNeighboringColumnIndex
+    },
+
+    _resizerDragging: function(event)
+    {
+        var resizer = this._currentResizer;
+        if (!resizer)
+            return;
+
+        var tableWidth = this._dataTable.offsetWidth; // Cache it early, before we invalidate layout.
+
+        // Constrain the dragpoint to be within the containing div of the
+        // datagrid.
+        var dragPoint = event.clientX - this.element.totalOffsetLeft();
+        // Constrain the dragpoint to be within the space made up by the
+        // column directly to the left and the column directly to the right.
+        var leftCellIndex = resizer.leftNeighboringColumnIndex;
+        var rightCellIndex = resizer.rightNeighboringColumnIndex;
+        var firstRowCells = this.headerTableBody.rows[0].cells;
+        var leftEdgeOfPreviousColumn = 0;
+        for (var i = 0; i < leftCellIndex; i++)
+            leftEdgeOfPreviousColumn += firstRowCells[i].offsetWidth;
+
+        // Differences for other resize methods
+        if (this.resizeMethod == WebInspector.DataGrid.ResizeMethod.Last) {
+            rightCellIndex = this.resizers.length;
+        } else if (this.resizeMethod == WebInspector.DataGrid.ResizeMethod.First) {
+            leftEdgeOfPreviousColumn += firstRowCells[leftCellIndex].offsetWidth - firstRowCells[0].offsetWidth;
+            leftCellIndex = 0;
+        }
+
+        var rightEdgeOfNextColumn = leftEdgeOfPreviousColumn + firstRowCells[leftCellIndex].offsetWidth + firstRowCells[rightCellIndex].offsetWidth;
+
+        // Give each column some padding so that they don't disappear.
+        var leftMinimum = leftEdgeOfPreviousColumn + this.ColumnResizePadding;
+        var rightMaximum = rightEdgeOfNextColumn - this.ColumnResizePadding;
+        if (leftMinimum > rightMaximum)
+            return;
+
+        dragPoint = Number.constrain(dragPoint, leftMinimum, rightMaximum);
+
+        resizer.style.left = (dragPoint - this.CenterResizerOverBorderAdjustment) + "px";
+
+        var percentLeftColumn = (((dragPoint - leftEdgeOfPreviousColumn) / tableWidth) * 100) + "%";
+        this._headerTableColumnGroup.children[leftCellIndex].style.width = percentLeftColumn;
+        this._dataTableColumnGroup.children[leftCellIndex].style.width = percentLeftColumn;
+
+        var percentRightColumn = (((rightEdgeOfNextColumn - dragPoint) / tableWidth) * 100) + "%";
+        this._headerTableColumnGroup.children[rightCellIndex].style.width =  percentRightColumn;
+        this._dataTableColumnGroup.children[rightCellIndex].style.width = percentRightColumn;
+
+        var leftColumn = this._columnsArray[leftCellIndex];
+        var rightColumn = this._columnsArray[rightCellIndex];
+        if (leftColumn.weight || rightColumn.weight) {
+            var sumOfWeights = leftColumn.weight + rightColumn.weight;
+            var delta = rightEdgeOfNextColumn - leftEdgeOfPreviousColumn;
+            leftColumn.weight = (dragPoint - leftEdgeOfPreviousColumn) * sumOfWeights / delta;
+            rightColumn.weight = (rightEdgeOfNextColumn - dragPoint) * sumOfWeights / delta;
+        }
+
+        this._positionResizers();
+        event.preventDefault();
+        this.dispatchEventToListeners(WebInspector.DataGrid.Events.ColumnsResized);
+    },
+
+    _endResizerDragging: function(event)
+    {
+        this._currentResizer = null;
+        this._saveColumnWeights();
+        this.dispatchEventToListeners(WebInspector.DataGrid.Events.ColumnsResized);
+    },
+
+    ColumnResizePadding: 24,
+
+    CenterResizerOverBorderAdjustment: 3,
+
+    __proto__: WebInspector.View.prototype
+}
+
+WebInspector.DataGrid.ResizeMethod = {
+    Nearest: "nearest",
+    First: "first",
+    Last: "last"
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {*=} data
+ * @param {boolean=} hasChildren
+ */
+WebInspector.DataGridNode = function(data, hasChildren)
+{
+    this._expanded = false;
+    this._selected = false;
+    this._shouldRefreshChildren = true;
+    this._data = data || {};
+    this.hasChildren = hasChildren || false;
+    /** @type {!Array.<WebInspector.DataGridNode>} */
+    this.children = [];
+    this.dataGrid = null;
+    this.parent = null;
+    /** @type {WebInspector.DataGridNode} */
+    this.previousSibling = null;
+    /** @type {WebInspector.DataGridNode} */
+    this.nextSibling = null;
+    this.disclosureToggleWidth = 10;
+}
+
+WebInspector.DataGridNode.prototype = {
+    /** @type {boolean} */
+    selectable: true,
+
+    /** @type {boolean} */
+    _isRoot: false,
+
+    get element()
+    {
+        if (this._element)
+            return this._element;
+
+        if (!this.dataGrid)
+            return null;
+
+        this._element = document.createElement("tr");
+        this._element._dataGridNode = this;
+
+        if (this.hasChildren)
+            this._element.addStyleClass("parent");
+        if (this.expanded)
+            this._element.addStyleClass("expanded");
+        if (this.selected)
+            this._element.addStyleClass("selected");
+        if (this.revealed)
+            this._element.addStyleClass("revealed");
+
+        this.createCells();
+        this._element.createChild("td", "corner");
+
+        return this._element;
+    },
+
+    createCells: function()
+    {
+        var columnsArray = this.dataGrid._columnsArray;
+        for (var i = 0; i < columnsArray.length; ++i) {
+            var cell = this.createCell(columnsArray[i].identifier);
+            this._element.appendChild(cell);
+        }
+    },
+
+    get data()
+    {
+        return this._data;
+    },
+
+    set data(x)
+    {
+        this._data = x || {};
+        this.refresh();
+    },
+
+    get revealed()
+    {
+        if ("_revealed" in this)
+            return this._revealed;
+
+        var currentAncestor = this.parent;
+        while (currentAncestor && !currentAncestor._isRoot) {
+            if (!currentAncestor.expanded) {
+                this._revealed = false;
+                return false;
+            }
+
+            currentAncestor = currentAncestor.parent;
+        }
+
+        this._revealed = true;
+        return true;
+    },
+
+    set hasChildren(x)
+    {
+        if (this._hasChildren === x)
+            return;
+
+        this._hasChildren = x;
+
+        if (!this._element)
+            return;
+
+        this._element.enableStyleClass("parent", this._hasChildren);
+        this._element.enableStyleClass("expanded", this._hasChildren && this.expanded);
+    },
+
+    get hasChildren()
+    {
+        return this._hasChildren;
+    },
+
+    set revealed(x)
+    {
+        if (this._revealed === x)
+            return;
+
+        this._revealed = x;
+
+        if (this._element)
+            this._element.enableStyleClass("revealed", this._revealed);
+
+        for (var i = 0; i < this.children.length; ++i)
+            this.children[i].revealed = x && this.expanded;
+    },
+
+    get depth()
+    {
+        if ("_depth" in this)
+            return this._depth;
+        if (this.parent && !this.parent._isRoot)
+            this._depth = this.parent.depth + 1;
+        else
+            this._depth = 0;
+        return this._depth;
+    },
+
+    get leftPadding()
+    {
+        if (typeof this._leftPadding === "number")
+            return this._leftPadding;
+
+        this._leftPadding = this.depth * this.dataGrid.indentWidth;
+        return this._leftPadding;
+    },
+
+    get shouldRefreshChildren()
+    {
+        return this._shouldRefreshChildren;
+    },
+
+    set shouldRefreshChildren(x)
+    {
+        this._shouldRefreshChildren = x;
+        if (x && this.expanded)
+            this.expand();
+    },
+
+    get selected()
+    {
+        return this._selected;
+    },
+
+    set selected(x)
+    {
+        if (x)
+            this.select();
+        else
+            this.deselect();
+    },
+
+    get expanded()
+    {
+        return this._expanded;
+    },
+
+    set expanded(x)
+    {
+        if (x)
+            this.expand();
+        else
+            this.collapse();
+    },
+
+    refresh: function()
+    {
+        if (!this._element || !this.dataGrid)
+            return;
+
+        this._element.removeChildren();
+        this.createCells();
+        this._element.createChild("td", "corner");
+    },
+
+    /**
+     * @param {string} columnIdentifier
+     * @return {!Element}
+     */
+    createTD: function(columnIdentifier)
+    {
+        var cell = document.createElement("td");
+        cell.className = columnIdentifier + "-column";
+        cell.columnIdentifier_ = columnIdentifier;
+
+        var alignment = this.dataGrid.columns[columnIdentifier].align;
+        if (alignment)
+            cell.addStyleClass(alignment);
+
+        return cell;
+    },
+
+    /**
+     * @param {string} columnIdentifier
+     * @return {!Element}
+     */
+    createCell: function(columnIdentifier)
+    {
+        var cell = this.createTD(columnIdentifier);
+
+        var data = this.data[columnIdentifier];
+        var div = document.createElement("div");
+        if (data instanceof Node)
+            div.appendChild(data);
+        else {
+            div.textContent = data;
+            if (this.dataGrid.columns[columnIdentifier].longText)
+                div.title = data;
+        }
+        cell.appendChild(div);
+
+        if (columnIdentifier === this.dataGrid.disclosureColumnIdentifier) {
+            cell.addStyleClass("disclosure");
+            if (this.leftPadding)
+                cell.style.setProperty("padding-left", this.leftPadding + "px");
+        }
+
+        return cell;
+    },
+
+    /**
+     * @return {number}
+     */
+    nodeHeight: function()
+    {
+        var rowHeight = 16;
+        if (!this.revealed)
+            return 0;
+        if (!this.expanded)
+            return rowHeight;
+        var result = rowHeight;
+        for (var i = 0; i < this.children.length; i++)
+            result += this.children[i].nodeHeight();
+        return result;
+    },
+
+    /**
+     * @param {WebInspector.DataGridNode} child
+     */
+    appendChild: function(child)
+    {
+        this.insertChild(child, this.children.length);
+    },
+
+    /**
+     * @param {WebInspector.DataGridNode} child
+     * @param {number} index
+     */
+    insertChild: function(child, index)
+    {
+        if (!child)
+            throw("insertChild: Node can't be undefined or null.");
+        if (child.parent === this)
+            throw("insertChild: Node is already a child of this node.");
+
+        if (child.parent)
+            child.parent.removeChild(child);
+
+        this.children.splice(index, 0, child);
+        this.hasChildren = true;
+
+        child.parent = this;
+        child.dataGrid = this.dataGrid;
+        child._recalculateSiblings(index);
+
+        delete child._depth;
+        delete child._revealed;
+        delete child._attached;
+        child._shouldRefreshChildren = true;
+
+        var current = child.children[0];
+        while (current) {
+            current.dataGrid = this.dataGrid;
+            delete current._depth;
+            delete current._revealed;
+            delete current._attached;
+            current._shouldRefreshChildren = true;
+            current = current.traverseNextNode(false, child, true);
+        }
+
+        if (this.expanded)
+            child._attach();
+        if (!this.revealed)
+            child.revealed = false;
+    },
+
+    /**
+     * @param {WebInspector.DataGridNode} child
+     */
+    removeChild: function(child)
+    {
+        if (!child)
+            throw("removeChild: Node can't be undefined or null.");
+        if (child.parent !== this)
+            throw("removeChild: Node is not a child of this node.");
+
+        child.deselect();
+        child._detach();
+
+        this.children.remove(child, true);
+
+        if (child.previousSibling)
+            child.previousSibling.nextSibling = child.nextSibling;
+        if (child.nextSibling)
+            child.nextSibling.previousSibling = child.previousSibling;
+
+        child.dataGrid = null;
+        child.parent = null;
+        child.nextSibling = null;
+        child.previousSibling = null;
+
+        if (this.children.length <= 0)
+            this.hasChildren = false;
+    },
+
+    removeChildren: function()
+    {
+        for (var i = 0; i < this.children.length; ++i) {
+            var child = this.children[i];
+            child.deselect();
+            child._detach();
+
+            child.dataGrid = null;
+            child.parent = null;
+            child.nextSibling = null;
+            child.previousSibling = null;
+        }
+
+        this.children = [];
+        this.hasChildren = false;
+    },
+
+    _recalculateSiblings: function(myIndex)
+    {
+        if (!this.parent)
+            return;
+
+        var previousChild = (myIndex > 0 ? this.parent.children[myIndex - 1] : null);
+
+        if (previousChild) {
+            previousChild.nextSibling = this;
+            this.previousSibling = previousChild;
+        } else
+            this.previousSibling = null;
+
+        var nextChild = this.parent.children[myIndex + 1];
+
+        if (nextChild) {
+            nextChild.previousSibling = this;
+            this.nextSibling = nextChild;
+        } else
+            this.nextSibling = null;
+    },
+
+    collapse: function()
+    {
+        if (this._isRoot)
+            return;
+        if (this._element)
+            this._element.removeStyleClass("expanded");
+
+        this._expanded = false;
+
+        for (var i = 0; i < this.children.length; ++i)
+            this.children[i].revealed = false;
+    },
+
+    collapseRecursively: function()
+    {
+        var item = this;
+        while (item) {
+            if (item.expanded)
+                item.collapse();
+            item = item.traverseNextNode(false, this, true);
+        }
+    },
+
+    populate: function() { },
+
+    expand: function()
+    {
+        if (!this.hasChildren || this.expanded)
+            return;
+        if (this._isRoot)
+            return;
+
+        if (this.revealed && !this._shouldRefreshChildren)
+            for (var i = 0; i < this.children.length; ++i)
+                this.children[i].revealed = true;
+
+        if (this._shouldRefreshChildren) {
+            for (var i = 0; i < this.children.length; ++i)
+                this.children[i]._detach();
+
+            this.populate();
+
+            if (this._attached) {
+                for (var i = 0; i < this.children.length; ++i) {
+                    var child = this.children[i];
+                    if (this.revealed)
+                        child.revealed = true;
+                    child._attach();
+                }
+            }
+
+            delete this._shouldRefreshChildren;
+        }
+
+        if (this._element)
+            this._element.addStyleClass("expanded");
+
+        this._expanded = true;
+    },
+
+    expandRecursively: function()
+    {
+        var item = this;
+        while (item) {
+            item.expand();
+            item = item.traverseNextNode(false, this);
+        }
+    },
+
+    reveal: function()
+    {
+        if (this._isRoot)
+            return;
+        var currentAncestor = this.parent;
+        while (currentAncestor && !currentAncestor._isRoot) {
+            if (!currentAncestor.expanded)
+                currentAncestor.expand();
+            currentAncestor = currentAncestor.parent;
+        }
+
+        this.element.scrollIntoViewIfNeeded(false);
+    },
+
+    /**
+     * @param {boolean=} supressSelectedEvent
+     */
+    select: function(supressSelectedEvent)
+    {
+        if (!this.dataGrid || !this.selectable || this.selected)
+            return;
+
+        if (this.dataGrid.selectedNode)
+            this.dataGrid.selectedNode.deselect();
+
+        this._selected = true;
+        this.dataGrid.selectedNode = this;
+
+        if (this._element)
+            this._element.addStyleClass("selected");
+
+        if (!supressSelectedEvent)
+            this.dataGrid.dispatchEventToListeners(WebInspector.DataGrid.Events.SelectedNode);
+    },
+
+    revealAndSelect: function()
+    {
+        if (this._isRoot)
+            return;
+        this.reveal();
+        this.select();
+    },
+
+    /**
+     * @param {boolean=} supressDeselectedEvent
+     */
+    deselect: function(supressDeselectedEvent)
+    {
+        if (!this.dataGrid || this.dataGrid.selectedNode !== this || !this.selected)
+            return;
+
+        this._selected = false;
+        this.dataGrid.selectedNode = null;
+
+        if (this._element)
+            this._element.removeStyleClass("selected");
+
+        if (!supressDeselectedEvent)
+            this.dataGrid.dispatchEventToListeners(WebInspector.DataGrid.Events.DeselectedNode);
+    },
+
+    /**
+     * @param {boolean} skipHidden
+     * @param {WebInspector.DataGridNode=} stayWithin
+     * @param {boolean=} dontPopulate
+     * @param {Object=} info
+     * @return {WebInspector.DataGridNode}
+     */
+    traverseNextNode: function(skipHidden, stayWithin, dontPopulate, info)
+    {
+        if (!dontPopulate && this.hasChildren)
+            this.populate();
+
+        if (info)
+            info.depthChange = 0;
+
+        var node = (!skipHidden || this.revealed) ? this.children[0] : null;
+        if (node && (!skipHidden || this.expanded)) {
+            if (info)
+                info.depthChange = 1;
+            return node;
+        }
+
+        if (this === stayWithin)
+            return null;
+
+        node = (!skipHidden || this.revealed) ? this.nextSibling : null;
+        if (node)
+            return node;
+
+        node = this;
+        while (node && !node._isRoot && !((!skipHidden || node.revealed) ? node.nextSibling : null) && node.parent !== stayWithin) {
+            if (info)
+                info.depthChange -= 1;
+            node = node.parent;
+        }
+
+        if (!node)
+            return null;
+
+        return (!skipHidden || node.revealed) ? node.nextSibling : null;
+    },
+
+    /**
+     * @param {boolean} skipHidden
+     * @param {boolean=} dontPopulate
+     * @return {WebInspector.DataGridNode}
+     */
+    traversePreviousNode: function(skipHidden, dontPopulate)
+    {
+        var node = (!skipHidden || this.revealed) ? this.previousSibling : null;
+        if (!dontPopulate && node && node.hasChildren)
+            node.populate();
+
+        while (node && ((!skipHidden || (node.revealed && node.expanded)) ? node.children[node.children.length - 1] : null)) {
+            if (!dontPopulate && node.hasChildren)
+                node.populate();
+            node = ((!skipHidden || (node.revealed && node.expanded)) ? node.children[node.children.length - 1] : null);
+        }
+
+        if (node)
+            return node;
+
+        if (!this.parent || this.parent._isRoot)
+            return null;
+
+        return this.parent;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isEventWithinDisclosureTriangle: function(event)
+    {
+        if (!this.hasChildren)
+            return false;
+        var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
+        if (!cell.hasStyleClass("disclosure"))
+            return false;
+
+        var left = cell.totalOffsetLeft() + this.leftPadding;
+        return event.pageX >= left && event.pageX <= left + this.disclosureToggleWidth;
+    },
+
+    _attach: function()
+    {
+        if (!this.dataGrid || this._attached)
+            return;
+
+        this._attached = true;
+
+        var nextNode = null;
+        var previousNode = this.traversePreviousNode(true, true);
+        if (previousNode && previousNode.element.parentNode && previousNode.element.nextSibling)
+            nextNode = previousNode.element.nextSibling;
+        if (!nextNode)
+            nextNode = this.dataGrid.dataTableBody.firstChild;
+        this.dataGrid.dataTableBody.insertBefore(this.element, nextNode);
+
+        if (this.expanded)
+            for (var i = 0; i < this.children.length; ++i)
+                this.children[i]._attach();
+    },
+
+    _detach: function()
+    {
+        if (!this._attached)
+            return;
+
+        this._attached = false;
+
+        if (this._element && this._element.parentNode)
+            this._element.parentNode.removeChild(this._element);
+
+        for (var i = 0; i < this.children.length; ++i)
+            this.children[i]._detach();
+
+        this.wasDetached();
+    },
+
+    wasDetached: function()
+    {
+    },
+
+    savePosition: function()
+    {
+        if (this._savedPosition)
+            return;
+
+        if (!this.parent)
+            throw("savePosition: Node must have a parent.");
+        this._savedPosition = {
+            parent: this.parent,
+            index: this.parent.children.indexOf(this)
+        };
+    },
+
+    restorePosition: function()
+    {
+        if (!this._savedPosition)
+            return;
+
+        if (this.parent !== this._savedPosition.parent)
+            this._savedPosition.parent.insertChild(this, this._savedPosition.index);
+
+        delete this._savedPosition;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ */
+WebInspector.CreationDataGridNode = function(data, hasChildren)
+{
+    WebInspector.DataGridNode.call(this, data, hasChildren);
+    this.isCreationNode = true;
+}
+
+WebInspector.CreationDataGridNode.prototype = {
+    makeNormal: function()
+    {
+        delete this.isCreationNode;
+        delete this.makeNormal;
+    },
+
+    __proto__: WebInspector.DataGridNode.prototype
+}
diff --git a/Source/devtools/front_end/Database.js b/Source/devtools/front_end/Database.js
new file mode 100644
index 0000000..9758c25
--- /dev/null
+++ b/Source/devtools/front_end/Database.js
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2007, 2008 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.DatabaseModel} model
+ */
+WebInspector.Database = function(model, id, domain, name, version)
+{
+    this._model = model;
+    this._id = id;
+    this._domain = domain;
+    this._name = name;
+    this._version = version;
+}
+
+WebInspector.Database.prototype = {
+    /** @return {string} */
+    get id()
+    {
+        return this._id;
+    },
+
+    /** @return {string} */
+    get name()
+    {
+        return this._name;
+    },
+
+    set name(x)
+    {
+        this._name = x;
+    },
+
+    /** @return {string} */
+    get version()
+    {
+        return this._version;
+    },
+
+    set version(x)
+    {
+        this._version = x;
+    },
+
+    /** @return {string} */
+    get domain()
+    {
+        return this._domain;
+    },
+
+    set domain(x)
+    {
+        this._domain = x;
+    },
+
+    /**
+     * @param {function(Array.<string>)} callback
+     */
+    getTableNames: function(callback)
+    {
+        function sortingCallback(error, names)
+        {
+            if (!error)
+                callback(names.sort());
+        }
+        DatabaseAgent.getDatabaseTableNames(this._id, sortingCallback);
+    },
+
+    /**
+     * @param {string} query
+     * @param {function(Array.<string>=, Array.<*>=)} onSuccess
+     * @param {function(string)} onError
+     */
+    executeSql: function(query, onSuccess, onError)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {Array.<string>=} columnNames
+         * @param {Array.<*>=} values
+         * @param {DatabaseAgent.Error=} errorObj
+         */
+        function callback(error, columnNames, values, errorObj)
+        {
+            if (error) {
+                onError(error);
+                return;
+            }
+            if (errorObj) {
+                var message;
+                if (errorObj.message)
+                    message = errorObj.message;
+                else if (errorObj.code == 2)
+                    message = WebInspector.UIString("Database no longer has expected version.");
+                else
+                    message = WebInspector.UIString("An unexpected error %s occurred.", errorObj.code);
+                onError(message);
+                return;
+            }
+            onSuccess(columnNames, values);
+        }
+        DatabaseAgent.executeSQL(this._id, query, callback.bind(this));
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.DatabaseModel = function()
+{
+    this._databases = [];
+    InspectorBackend.registerDatabaseDispatcher(new WebInspector.DatabaseDispatcher(this));
+    DatabaseAgent.enable();
+}
+
+WebInspector.DatabaseModel.Events = {
+    DatabaseAdded: "DatabaseAdded"
+}
+
+WebInspector.DatabaseModel.prototype = {
+    /**
+     * @return {Array.<WebInspector.Database>}
+     */
+    databases: function()
+    {
+        var result = [];
+        for (var databaseId in this._databases)
+            result.push(this._databases[databaseId]);
+        return result;
+    },
+
+    /**
+     * @param {DatabaseAgent.DatabaseId} databaseId
+     * @return {WebInspector.Database}
+     */
+    databaseForId: function(databaseId)
+    {
+        return this._databases[databaseId];
+    },
+
+    /**
+     * @param {WebInspector.Database} database
+     */
+    _addDatabase: function(database)
+    {
+        this._databases.push(database);
+        this.dispatchEventToListeners(WebInspector.DatabaseModel.Events.DatabaseAdded, database);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {DatabaseAgent.Dispatcher}
+ * @param {WebInspector.DatabaseModel} model
+ */
+WebInspector.DatabaseDispatcher = function(model)
+{
+    this._model = model;
+}
+
+WebInspector.DatabaseDispatcher.prototype = {
+    /**
+     * @param {DatabaseAgent.Database} payload
+     */
+    addDatabase: function(payload)
+    {
+        this._model._addDatabase(new WebInspector.Database(
+            this._model,
+            payload.id,
+            payload.domain,
+            payload.name,
+            payload.version));
+    }
+}
+
+/**
+ * @type {WebInspector.DatabaseModel}
+ */
+WebInspector.databaseModel = null;
diff --git a/Source/devtools/front_end/DatabaseQueryView.js b/Source/devtools/front_end/DatabaseQueryView.js
new file mode 100644
index 0000000..fd6ce9f
--- /dev/null
+++ b/Source/devtools/front_end/DatabaseQueryView.js
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.DatabaseQueryView = function(database)
+{
+    WebInspector.View.call(this);
+
+    this.database = database;
+
+    this.element.addStyleClass("storage-view");
+    this.element.addStyleClass("query");
+    this.element.addStyleClass("monospace");
+    this.element.addEventListener("selectstart", this._selectStart.bind(this), false);
+
+    this._promptElement = document.createElement("div");
+    this._promptElement.className = "database-query-prompt";
+    this._promptElement.appendChild(document.createElement("br"));
+    this._promptElement.addEventListener("keydown", this._promptKeyDown.bind(this), true);
+    this.element.appendChild(this._promptElement);
+
+    this.prompt = new WebInspector.TextPromptWithHistory(this.completions.bind(this), " ");
+    this.prompt.attach(this._promptElement);
+
+    this.element.addEventListener("click", this._messagesClicked.bind(this), true);
+}
+
+WebInspector.DatabaseQueryView.Events = {
+    SchemaUpdated: "SchemaUpdated"
+}
+
+WebInspector.DatabaseQueryView.prototype = {
+    _messagesClicked: function()
+    {
+        if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
+            this.prompt.moveCaretToEndOfPrompt();
+    },
+    
+    /**
+     * @param {Element} proxyElement
+     * @param {Range} wordRange
+     * @param {boolean} force
+     * @param {function(!Array.<string>, number=)} completionsReadyCallback
+     */
+    completions: function(proxyElement, wordRange, force, completionsReadyCallback)
+    {
+        var prefix = wordRange.toString().toLowerCase();
+        if (!prefix.length && !force)
+            return;
+
+        var results = [];
+
+        function accumulateMatches(textArray)
+        {
+            for (var i = 0; i < textArray.length; ++i) {
+                var text = textArray[i].toLowerCase();
+                if (text.length < prefix.length)
+                    continue;
+                if (!text.startsWith(prefix))
+                    continue;
+                results.push(textArray[i]);
+            }
+        }
+
+        function tableNamesCallback(tableNames)
+        {
+            accumulateMatches(tableNames.map(function(name) { return name + " " }));
+            accumulateMatches(["SELECT ", "FROM ", "WHERE ", "LIMIT ", "DELETE FROM ", "CREATE ", "DROP ", "TABLE ", "INDEX ", "UPDATE ", "INSERT INTO ", "VALUES ("]);
+
+            completionsReadyCallback(results);
+        }
+        this.database.getTableNames(tableNamesCallback);
+    },
+
+    _selectStart: function(event)
+    {
+        if (this._selectionTimeout)
+            clearTimeout(this._selectionTimeout);
+
+        this.prompt.clearAutoComplete();
+
+        function moveBackIfOutside()
+        {
+            delete this._selectionTimeout;
+            if (!this.prompt.isCaretInsidePrompt() && window.getSelection().isCollapsed)
+                this.prompt.moveCaretToEndOfPrompt();
+            this.prompt.autoCompleteSoon();
+        }
+
+        this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100);
+    },
+
+    _promptKeyDown: function(event)
+    {
+        if (isEnterKey(event)) {
+            this._enterKeyPressed(event);
+            return;
+        }
+    },
+
+    _enterKeyPressed: function(event)
+    {
+        event.consume(true);
+
+        this.prompt.clearAutoComplete(true);
+
+        var query = this.prompt.text;
+        if (!query.length)
+            return;
+
+        this.prompt.pushHistoryItem(query);
+        this.prompt.text = "";
+
+        this.database.executeSql(query, this._queryFinished.bind(this, query), this._queryError.bind(this, query));
+    },
+
+    _queryFinished: function(query, columnNames, values)
+    {
+        var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, values);
+        var trimmedQuery = query.trim();
+
+        if (dataGrid) {
+            dataGrid.renderInline();
+            this._appendViewQueryResult(trimmedQuery, dataGrid);
+            dataGrid.autoSizeColumns(5);
+        }
+
+        if (trimmedQuery.match(/^create /i) || trimmedQuery.match(/^drop table /i))
+            this.dispatchEventToListeners(WebInspector.DatabaseQueryView.Events.SchemaUpdated, this.database);
+    },
+
+    _queryError: function(query, errorMessage)
+    {
+        this._appendErrorQueryResult(query, errorMessage);
+    },
+
+    /**
+     * @param {string} query
+     * @param {WebInspector.View} view
+     */
+    _appendViewQueryResult: function(query, view)
+    {
+        var resultElement = this._appendQueryResult(query);
+        view.show(resultElement);
+
+        this._promptElement.scrollIntoView(false);
+    },
+
+    /**
+     * @param {string} query
+     * @param {string} errorText
+     */
+    _appendErrorQueryResult: function(query, errorText)
+    {
+        var resultElement = this._appendQueryResult(query);
+        resultElement.addStyleClass("error")
+        resultElement.textContent = errorText;
+
+        this._promptElement.scrollIntoView(false);
+    },
+
+    _appendQueryResult: function(query)
+    {
+        var element = document.createElement("div");
+        element.className = "database-user-query";
+        this.element.insertBefore(element, this.prompt.proxyElement);
+
+        var commandTextElement = document.createElement("span");
+        commandTextElement.className = "database-query-text";
+        commandTextElement.textContent = query;
+        element.appendChild(commandTextElement);
+
+        var resultElement = document.createElement("div");
+        resultElement.className = "database-query-result";
+        element.appendChild(resultElement);
+        return resultElement;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/DatabaseTableView.js b/Source/devtools/front_end/DatabaseTableView.js
new file mode 100644
index 0000000..0d5e5f8
--- /dev/null
+++ b/Source/devtools/front_end/DatabaseTableView.js
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.DatabaseTableView = function(database, tableName)
+{
+    WebInspector.View.call(this);
+
+    this.database = database;
+    this.tableName = tableName;
+
+    this.element.addStyleClass("storage-view");
+    this.element.addStyleClass("table");
+
+    this.refreshButton = new WebInspector.StatusBarButton(WebInspector.UIString("Refresh"), "refresh-storage-status-bar-item");
+    this.refreshButton.addEventListener("click", this._refreshButtonClicked, this);
+}
+
+WebInspector.DatabaseTableView.prototype = {
+    wasShown: function()
+    {
+        this.update();
+    },
+
+    get statusBarItems()
+    {
+        return [this.refreshButton.element];
+    },
+
+    /**
+     * @param {string} tableName
+     * @return {string}
+     */
+    _escapeTableName: function(tableName)
+    {
+        return tableName.replace(/\"/g, "\"\"");
+    },
+    
+    update: function()
+    {
+        this.database.executeSql("SELECT * FROM \"" + this._escapeTableName(this.tableName) + "\"", this._queryFinished.bind(this), this._queryError.bind(this));
+    },
+
+    _queryFinished: function(columnNames, values)
+    {
+        this.detachChildViews();
+        this.element.removeChildren();
+
+        var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, values);
+        if (!dataGrid) {
+            this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("The “%s”\ntable is empty.", this.tableName));
+            this._emptyView.show(this.element);
+            return;
+        }
+        dataGrid.show(this.element);
+        dataGrid.autoSizeColumns(5);
+    },
+
+    _queryError: function(error)
+    {
+        this.detachChildViews();
+        this.element.removeChildren();
+
+        var errorMsgElement = document.createElement("div");
+        errorMsgElement.className = "storage-table-error";
+        errorMsgElement.textContent = WebInspector.UIString("An error occurred trying to\nread the “%s” table.", this.tableName);
+        this.element.appendChild(errorMsgElement);
+    },
+
+    _refreshButtonClicked: function(event)
+    {
+        this.update();
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/DebuggerModel.js b/Source/devtools/front_end/DebuggerModel.js
new file mode 100644
index 0000000..fdf6d8a
--- /dev/null
+++ b/Source/devtools/front_end/DebuggerModel.js
@@ -0,0 +1,849 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.DebuggerModel = function()
+{
+    InspectorBackend.registerDebuggerDispatcher(new WebInspector.DebuggerDispatcher(this));
+
+    this._debuggerPausedDetails = null;
+    /**
+     * @type {Object.<string, WebInspector.Script>}
+     */
+    this._scripts = {};
+    this._scriptsBySourceURL = {};
+
+    this._canSetScriptSource = false;
+    this._breakpointsActive = true;
+
+    WebInspector.settings.pauseOnExceptionStateString = WebInspector.settings.createSetting("pauseOnExceptionStateString", WebInspector.DebuggerModel.PauseOnExceptionsState.DontPauseOnExceptions);
+    WebInspector.settings.pauseOnExceptionStateString.addChangeListener(this._pauseOnExceptionStateChanged, this);
+
+    this.enableDebugger();
+}
+
+// Keep these in sync with WebCore::ScriptDebugServer
+WebInspector.DebuggerModel.PauseOnExceptionsState = {
+    DontPauseOnExceptions : "none",
+    PauseOnAllExceptions : "all",
+    PauseOnUncaughtExceptions: "uncaught"
+};
+
+/**
+ * @constructor
+ * @implements {WebInspector.RawLocation}
+ * @param {string} scriptId
+ * @param {number} lineNumber
+ * @param {number} columnNumber
+ */
+WebInspector.DebuggerModel.Location = function(scriptId, lineNumber, columnNumber)
+{
+    this.scriptId = scriptId;
+    this.lineNumber = lineNumber;
+    this.columnNumber = columnNumber;
+}
+
+WebInspector.DebuggerModel.Events = {
+    DebuggerWasEnabled: "DebuggerWasEnabled",
+    DebuggerWasDisabled: "DebuggerWasDisabled",
+    DebuggerPaused: "DebuggerPaused",
+    DebuggerResumed: "DebuggerResumed",
+    ParsedScriptSource: "ParsedScriptSource",
+    FailedToParseScriptSource: "FailedToParseScriptSource",
+    BreakpointResolved: "BreakpointResolved",
+    GlobalObjectCleared: "GlobalObjectCleared",
+    CallFrameSelected: "CallFrameSelected",
+    ExecutionLineChanged: "ExecutionLineChanged",
+    ConsoleCommandEvaluatedInSelectedCallFrame: "ConsoleCommandEvaluatedInSelectedCallFrame",
+    BreakpointsActiveStateChanged: "BreakpointsActiveStateChanged"
+}
+
+WebInspector.DebuggerModel.BreakReason = {
+    DOM: "DOM",
+    EventListener: "EventListener",
+    XHR: "XHR",
+    Exception: "exception",
+    Assert: "assert",
+    CSPViolation: "CSPViolation"
+}
+
+WebInspector.DebuggerModel.prototype = {
+    /**
+     * @return {boolean}
+     */
+    debuggerEnabled: function()
+    {
+        return !!this._debuggerEnabled;
+    },
+
+    enableDebugger: function()
+    {
+        if (this._debuggerEnabled)
+            return;
+
+        function callback(error, result)
+        {
+            this._canSetScriptSource = result;
+        }
+        DebuggerAgent.canSetScriptSource(callback.bind(this));
+        DebuggerAgent.enable(this._debuggerWasEnabled.bind(this));
+    },
+
+    disableDebugger: function()
+    {
+        if (!this._debuggerEnabled)
+            return;
+
+        DebuggerAgent.disable(this._debuggerWasDisabled.bind(this));
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canSetScriptSource: function()
+    {
+        return this._canSetScriptSource;
+    },
+
+    _debuggerWasEnabled: function()
+    {
+        this._debuggerEnabled = true;
+        this._pauseOnExceptionStateChanged();
+        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasEnabled);
+    },
+
+    _pauseOnExceptionStateChanged: function()
+    {
+        DebuggerAgent.setPauseOnExceptions(WebInspector.settings.pauseOnExceptionStateString.get());
+    },
+
+    _debuggerWasDisabled: function()
+    {
+        this._debuggerEnabled = false;
+        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasDisabled);
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} rawLocation
+     */
+    continueToLocation: function(rawLocation)
+    {
+        DebuggerAgent.continueToLocation(rawLocation);
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} rawLocation
+     * @param {string} condition
+     * @param {function(?DebuggerAgent.BreakpointId, Array.<WebInspector.DebuggerModel.Location>):void=} callback
+     */
+    setBreakpointByScriptLocation: function(rawLocation, condition, callback)
+    {
+        var script = this.scriptForId(rawLocation.scriptId);
+        if (script.sourceURL)
+            this.setBreakpointByURL(script.sourceURL, rawLocation.lineNumber, rawLocation.columnNumber, condition, callback);
+        else
+            this.setBreakpointBySourceId(rawLocation, condition, callback);
+    },
+
+    /**
+     * @param {string} url
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     * @param {string=} condition
+     * @param {function(?DebuggerAgent.BreakpointId, Array.<WebInspector.DebuggerModel.Location>)=} callback
+     */
+    setBreakpointByURL: function(url, lineNumber, columnNumber, condition, callback)
+    {
+        // Adjust column if needed.
+        var minColumnNumber = 0;
+        var scripts = this._scriptsBySourceURL[url] || [];
+        for (var i = 0, l = scripts.length; i < l; ++i) {
+            var script = scripts[i];
+            if (lineNumber === script.lineOffset)
+                minColumnNumber = minColumnNumber ? Math.min(minColumnNumber, script.columnOffset) : script.columnOffset;
+        }
+        columnNumber = Math.max(columnNumber, minColumnNumber);
+
+        /**
+         * @this {WebInspector.DebuggerModel}
+         * @param {?Protocol.Error} error
+         * @param {DebuggerAgent.BreakpointId} breakpointId
+         * @param {Array.<DebuggerAgent.Location>} locations
+         */
+        function didSetBreakpoint(error, breakpointId, locations)
+        {
+            if (callback) {
+                var rawLocations = /** @type {Array.<WebInspector.DebuggerModel.Location>} */ (locations);
+                callback(error ? null : breakpointId, rawLocations);
+            }
+        }
+        DebuggerAgent.setBreakpointByUrl(lineNumber, url, undefined, columnNumber, condition, didSetBreakpoint.bind(this));
+        WebInspector.userMetrics.ScriptsBreakpointSet.record();
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} rawLocation
+     * @param {string} condition
+     * @param {function(?DebuggerAgent.BreakpointId, Array.<WebInspector.DebuggerModel.Location>)=} callback
+     */
+    setBreakpointBySourceId: function(rawLocation, condition, callback)
+    {
+        /**
+         * @this {WebInspector.DebuggerModel}
+         * @param {?Protocol.Error} error
+         * @param {DebuggerAgent.BreakpointId} breakpointId
+         * @param {DebuggerAgent.Location} actualLocation
+         */
+        function didSetBreakpoint(error, breakpointId, actualLocation)
+        {
+            if (callback) {
+                var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (actualLocation);
+                callback(error ? null : breakpointId, [rawLocation]);
+            }
+        }
+        DebuggerAgent.setBreakpoint(rawLocation, condition, didSetBreakpoint.bind(this));
+        WebInspector.userMetrics.ScriptsBreakpointSet.record();
+    },
+
+    /**
+     * @param {DebuggerAgent.BreakpointId} breakpointId
+     * @param {function(?Protocol.Error)=} callback
+     */
+    removeBreakpoint: function(breakpointId, callback)
+    {
+        DebuggerAgent.removeBreakpoint(breakpointId, callback);
+    },
+
+    /**
+     * @param {DebuggerAgent.BreakpointId} breakpointId
+     * @param {DebuggerAgent.Location} location
+     */
+    _breakpointResolved: function(breakpointId, location)
+    {
+        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointResolved, {breakpointId: breakpointId, location: location});
+    },
+
+    _globalObjectCleared: function()
+    {
+        this._setDebuggerPausedDetails(null);
+        this._reset();
+        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.GlobalObjectCleared);
+    },
+
+    _reset: function()
+    {
+        this._scripts = {};
+        this._scriptsBySourceURL = {};
+    },
+
+    /**
+     * @return {Object.<string, WebInspector.Script>}
+     */
+    get scripts()
+    {
+        return this._scripts;
+    },
+
+    /**
+     * @param {DebuggerAgent.ScriptId} scriptId
+     * @return {WebInspector.Script}
+     */
+    scriptForId: function(scriptId)
+    {
+        return this._scripts[scriptId] || null;
+    },
+
+    /**
+     * @param {DebuggerAgent.ScriptId} scriptId
+     * @param {string} newSource
+     * @param {function(?Protocol.Error)} callback
+     */
+    setScriptSource: function(scriptId, newSource, callback)
+    {
+        this._scripts[scriptId].editSource(newSource, this._didEditScriptSource.bind(this, scriptId, newSource, callback));
+    },
+
+    /**
+     * @param {DebuggerAgent.ScriptId} scriptId
+     * @param {string} newSource
+     * @param {function(?Protocol.Error)} callback
+     * @param {?Protocol.Error} error
+     * @param {Array.<DebuggerAgent.CallFrame>=} callFrames
+     */
+    _didEditScriptSource: function(scriptId, newSource, callback, error, callFrames)
+    {
+        callback(error);
+        if (!error && callFrames && callFrames.length)
+            this._pausedScript(callFrames, this._debuggerPausedDetails.reason, this._debuggerPausedDetails.auxData);
+    },
+
+    /**
+     * @return {Array.<DebuggerAgent.CallFrame>}
+     */
+    get callFrames()
+    {
+        return this._debuggerPausedDetails ? this._debuggerPausedDetails.callFrames : null;
+    },
+
+    /**
+     * @return {?WebInspector.DebuggerPausedDetails}
+     */
+    debuggerPausedDetails: function()
+    {
+        return this._debuggerPausedDetails;
+    },
+
+    /**
+     * @param {?WebInspector.DebuggerPausedDetails} debuggerPausedDetails
+     */
+    _setDebuggerPausedDetails: function(debuggerPausedDetails)
+    {
+        if (this._debuggerPausedDetails)
+            this._debuggerPausedDetails.dispose();
+        this._debuggerPausedDetails = debuggerPausedDetails;
+        if (this._debuggerPausedDetails)
+            this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPausedDetails);
+        if (debuggerPausedDetails) {
+            this.setSelectedCallFrame(debuggerPausedDetails.callFrames[0]);
+            DebuggerAgent.setOverlayMessage(WebInspector.UIString("Paused in debugger"));
+        } else {
+            this.setSelectedCallFrame(null);
+            DebuggerAgent.setOverlayMessage();
+        }
+    },
+
+    /**
+     * @param {Array.<DebuggerAgent.CallFrame>} callFrames
+     * @param {string} reason
+     * @param {*} auxData
+     */
+    _pausedScript: function(callFrames, reason, auxData)
+    {
+        this._setDebuggerPausedDetails(new WebInspector.DebuggerPausedDetails(this, callFrames, reason, auxData));
+    },
+
+    _resumedScript: function()
+    {
+        this._setDebuggerPausedDetails(null);
+        if (this._executionLineLiveLocation)
+            this._executionLineLiveLocation.dispose();
+        this._executionLineLiveLocation = null;
+        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed);
+    },
+
+    /**
+     * @param {DebuggerAgent.ScriptId} scriptId
+     * @param {string} sourceURL
+     * @param {number} startLine
+     * @param {number} startColumn
+     * @param {number} endLine
+     * @param {number} endColumn
+     * @param {boolean} isContentScript
+     * @param {string=} sourceMapURL
+     * @param {boolean=} hasSourceURL
+     */
+    _parsedScriptSource: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL)
+    {
+        var script = new WebInspector.Script(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL);
+        if (!script.isAnonymousScript() && !script.isInlineScript()) {
+            var existingScripts = this._scriptsBySourceURL[script.sourceURL] || [];
+            for (var i = 0; i < existingScripts.length; ++i) {
+                if (existingScripts[i].isInlineScript()) {
+                    script.setIsDynamicScript(true); 
+                    break;
+                }
+            }
+        }
+        this._registerScript(script);
+        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ParsedScriptSource, script);
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     */
+    _registerScript: function(script)
+    {
+        this._scripts[script.scriptId] = script;
+        if (script.sourceURL) {
+            var scripts = this._scriptsBySourceURL[script.sourceURL];
+            if (!scripts) {
+                scripts = [];
+                this._scriptsBySourceURL[script.sourceURL] = scripts;
+            }
+            scripts.push(script);
+        }
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.DebuggerModel.Location}
+     */
+    createRawLocation: function(script, lineNumber, columnNumber)
+    {
+        if (script.sourceURL)
+            return this.createRawLocationByURL(script.sourceURL, lineNumber, columnNumber)
+        return new WebInspector.DebuggerModel.Location(script.scriptId, lineNumber, columnNumber);
+    },
+
+    /**
+     * @param {string} sourceURL
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.DebuggerModel.Location}
+     */
+    createRawLocationByURL: function(sourceURL, lineNumber, columnNumber)
+    {
+        var closestScript = null;
+        var scripts = this._scriptsBySourceURL[sourceURL] || [];
+        for (var i = 0, l = scripts.length; i < l; ++i) {
+            var script = scripts[i];
+            if (!closestScript)
+                closestScript = script;
+            if (script.lineOffset > lineNumber || (script.lineOffset === lineNumber && script.columnOffset > columnNumber))
+                continue;
+            if (script.endLine < lineNumber || (script.endLine === lineNumber && script.endColumn <= columnNumber))
+                continue;
+            closestScript = script;
+            break;
+        }
+        return closestScript ? new WebInspector.DebuggerModel.Location(closestScript.scriptId, lineNumber, columnNumber) : null;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isPaused: function()
+    {
+        return !!this.debuggerPausedDetails();
+    },
+
+    /**
+     * @param {?WebInspector.DebuggerModel.CallFrame} callFrame
+     */
+    setSelectedCallFrame: function(callFrame)
+    {
+        if (this._executionLineLiveLocation)
+            this._executionLineLiveLocation.dispose();
+        delete this._executionLineLiveLocation;
+
+        this._selectedCallFrame = callFrame;
+        if (!this._selectedCallFrame)
+            return;
+
+        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.CallFrameSelected, callFrame);
+
+        function updateExecutionLine(uiLocation)
+        {
+            this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ExecutionLineChanged, uiLocation);
+        }
+        this._executionLineLiveLocation = callFrame.script.createLiveLocation(callFrame.location, updateExecutionLine.bind(this));
+    },
+
+    /**
+     * @return {?WebInspector.DebuggerModel.CallFrame}
+     */
+    selectedCallFrame: function()
+    {
+        return this._selectedCallFrame;
+    },
+
+    /**
+     * @param {string} code
+     * @param {string} objectGroup
+     * @param {boolean} includeCommandLineAPI
+     * @param {boolean} doNotPauseOnExceptionsAndMuteConsole
+     * @param {boolean} returnByValue
+     * @param {boolean} generatePreview
+     * @param {function(?WebInspector.RemoteObject, boolean, RuntimeAgent.RemoteObject=)} callback
+     */
+    evaluateOnSelectedCallFrame: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
+    {
+        /**
+         * @param {?RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function didEvaluate(result, wasThrown)
+        {
+            if (returnByValue)
+                callback(null, !!wasThrown, wasThrown ? null : result);
+            else
+                callback(WebInspector.RemoteObject.fromPayload(result), !!wasThrown);
+
+            if (objectGroup === "console")
+                this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame);
+        }
+
+        this.selectedCallFrame().evaluate(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluate.bind(this));
+    },
+
+    /**
+     * @param {function(Object)} callback
+     */
+    getSelectedCallFrameVariables: function(callback)
+    {
+        var result = { this: true };
+
+        var selectedCallFrame = this._selectedCallFrame;
+        if (!selectedCallFrame)
+            callback(result);
+
+        var pendingRequests = 0;
+
+        function propertiesCollected(properties)
+        {
+            for (var i = 0; properties && i < properties.length; ++i)
+                result[properties[i].name] = true;
+            if (--pendingRequests == 0)
+                callback(result);
+        }
+
+        for (var i = 0; i < selectedCallFrame.scopeChain.length; ++i) {
+            var scope = selectedCallFrame.scopeChain[i];
+            var object = WebInspector.RemoteObject.fromPayload(scope.object);
+            pendingRequests++;
+            object.getAllProperties(propertiesCollected);
+        }
+    },
+
+    /**
+     * @param {boolean} active
+     */
+    setBreakpointsActive: function(active)
+    {
+        if (this._breakpointsActive === active)
+            return;
+        this._breakpointsActive = active;
+        DebuggerAgent.setBreakpointsActive(active);
+        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointsActiveStateChanged, active);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    breakpointsActive: function()
+    {
+        return this._breakpointsActive;
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} rawLocation
+     * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
+     * @return {WebInspector.Script.Location}
+     */
+    createLiveLocation: function(rawLocation, updateDelegate)
+    {
+        var script = this._scripts[rawLocation.scriptId];
+        return script.createLiveLocation(rawLocation, updateDelegate);
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} rawLocation
+     * @return {?WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(rawLocation)
+    {
+        var script = this._scripts[rawLocation.scriptId];
+        if (!script)
+            return null;
+        return script.rawLocationToUILocation(rawLocation.lineNumber, rawLocation.columnNumber);
+    },
+
+    /**
+     * Handles notification from JavaScript VM about updated stack (liveedit or frame restart action).
+     * @this {WebInspector.DebuggerModel}
+     * @param {Array.<DebuggerAgent.CallFrame>=} newCallFrames
+     * @param {Object=} details
+     */
+    callStackModified: function(newCallFrames, details)
+    {
+        // FIXME: declare this property in protocol and in JavaScript.
+        if (details && details["stack_update_needs_step_in"])
+            DebuggerAgent.stepInto();
+        else {
+            if (newCallFrames && newCallFrames.length)
+                this._pausedScript(newCallFrames, this._debuggerPausedDetails.reason, this._debuggerPausedDetails.auxData);
+
+        }
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+WebInspector.DebuggerEventTypes = {
+    JavaScriptPause: 0,
+    JavaScriptBreakpoint: 1,
+    NativeBreakpoint: 2
+};
+
+/**
+ * @constructor
+ * @implements {DebuggerAgent.Dispatcher}
+ * @param {WebInspector.DebuggerModel} debuggerModel
+ */
+WebInspector.DebuggerDispatcher = function(debuggerModel)
+{
+    this._debuggerModel = debuggerModel;
+}
+
+WebInspector.DebuggerDispatcher.prototype = {
+    /**
+     * @param {Array.<DebuggerAgent.CallFrame>} callFrames
+     * @param {string} reason
+     * @param {Object=} auxData
+     */
+    paused: function(callFrames, reason, auxData)
+    {
+        this._debuggerModel._pausedScript(callFrames, reason, auxData);
+    },
+
+    resumed: function()
+    {
+        this._debuggerModel._resumedScript();
+    },
+
+    globalObjectCleared: function()
+    {
+        this._debuggerModel._globalObjectCleared();
+    },
+
+    /**
+     * @param {DebuggerAgent.ScriptId} scriptId
+     * @param {string} sourceURL
+     * @param {number} startLine
+     * @param {number} startColumn
+     * @param {number} endLine
+     * @param {number} endColumn
+     * @param {boolean=} isContentScript
+     * @param {string=} sourceMapURL
+     * @param {boolean=} hasSourceURL
+     */
+    scriptParsed: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL)
+    {
+        this._debuggerModel._parsedScriptSource(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, !!isContentScript, sourceMapURL, hasSourceURL);
+    },
+
+    /**
+     * @param {string} sourceURL
+     * @param {string} source
+     * @param {number} startingLine
+     * @param {number} errorLine
+     * @param {string} errorMessage
+     */
+    scriptFailedToParse: function(sourceURL, source, startingLine, errorLine, errorMessage)
+    {
+    },
+
+    /**
+    * @param {DebuggerAgent.BreakpointId} breakpointId
+    * @param {DebuggerAgent.Location} location
+     */
+    breakpointResolved: function(breakpointId, location)
+    {
+        this._debuggerModel._breakpointResolved(breakpointId, location);
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.Script} script
+ * @param {DebuggerAgent.CallFrame} payload
+ */
+WebInspector.DebuggerModel.CallFrame = function(script, payload)
+{
+    this._script = script;
+    this._payload = payload;
+    this._locations = [];
+}
+
+WebInspector.DebuggerModel.CallFrame.prototype = {
+    /**
+     * @return {WebInspector.Script}
+     */
+    get script()
+    {
+        return this._script;
+    },
+
+    /**
+     * @return {string}
+     */
+    get type()
+    {
+        return this._payload.type;
+    },
+
+    /**
+     * @return {string}
+     */
+    get id()
+    {
+        return this._payload.callFrameId;
+    },
+
+    /**
+     * @return {Array.<DebuggerAgent.Scope>}
+     */
+    get scopeChain()
+    {
+        return this._payload.scopeChain;
+    },
+
+    /**
+     * @return {RuntimeAgent.RemoteObject}
+     */
+    get this()
+    {
+        return this._payload.this;
+    },
+
+    /**
+     * @return {string}
+     */
+    get functionName()
+    {
+        return this._payload.functionName;
+    },
+
+    /**
+     * @return {WebInspector.DebuggerModel.Location}
+     */
+    get location()
+    {
+        var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this._payload.location);
+        return rawLocation;
+    },
+
+    /**
+     * @param {string} code
+     * @param {string} objectGroup
+     * @param {boolean} includeCommandLineAPI
+     * @param {boolean} doNotPauseOnExceptionsAndMuteConsole
+     * @param {boolean} returnByValue
+     * @param {boolean} generatePreview
+     * @param {function(?RuntimeAgent.RemoteObject, boolean=)=} callback
+     */
+    evaluate: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
+    {
+        /**
+         * @this {WebInspector.DebuggerModel.CallFrame}
+         * @param {?Protocol.Error} error
+         * @param {RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function didEvaluateOnCallFrame(error, result, wasThrown)
+        {
+            if (error) {
+                console.error(error);
+                callback(null, false);
+                return;
+            }
+            callback(result, wasThrown);
+        }
+        DebuggerAgent.evaluateOnCallFrame(this._payload.callFrameId, code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluateOnCallFrame.bind(this));
+    },
+
+    /**
+     * @param {function(?Protocol.Error=)=} callback
+     */
+    restart: function(callback)
+    {
+        /**
+         * @this {WebInspector.DebuggerModel.CallFrame}
+         * @param {?Protocol.Error} error
+         * @param {Array.<DebuggerAgent.CallFrame>=} callFrames
+         * @param {Object=} details
+         */
+        function protocolCallback(error, callFrames, details)
+        {
+            if (!error)
+                WebInspector.debuggerModel.callStackModified(callFrames, details);
+            if (callback)
+                callback(error);
+        }
+        DebuggerAgent.restartFrame(this._payload.callFrameId, protocolCallback);
+    },
+
+    /**
+     * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
+     */
+    createLiveLocation: function(updateDelegate)
+    {
+        var location = this._script.createLiveLocation(this.location, updateDelegate);
+        this._locations.push(location);
+        return location;
+    },
+
+    dispose: function(updateDelegate)
+    {
+        for (var i = 0; i < this._locations.length; ++i)
+            this._locations[i].dispose();
+        this._locations = [];
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.DebuggerModel} model
+ * @param {Array.<DebuggerAgent.CallFrame>} callFrames
+ * @param {string} reason
+ * @param {*} auxData
+ */
+WebInspector.DebuggerPausedDetails = function(model, callFrames, reason, auxData)
+{
+    this.callFrames = [];
+    for (var i = 0; i < callFrames.length; ++i) {
+        var callFrame = callFrames[i];
+        var script = model.scriptForId(callFrame.location.scriptId);
+        if (script)
+            this.callFrames.push(new WebInspector.DebuggerModel.CallFrame(script, callFrame));
+    }
+    this.reason = reason;
+    this.auxData = auxData;
+}
+
+WebInspector.DebuggerPausedDetails.prototype = {
+    dispose: function()
+    {
+        for (var i = 0; i < this.callFrames.length; ++i) {
+            var callFrame = this.callFrames[i];
+            callFrame.dispose();
+        }
+    }
+}
+
+/**
+ * @type {?WebInspector.DebuggerModel}
+ */
+WebInspector.debuggerModel = null;
diff --git a/Source/devtools/front_end/DebuggerScriptMapping.js b/Source/devtools/front_end/DebuggerScriptMapping.js
new file mode 100644
index 0000000..c6e7bd5
--- /dev/null
+++ b/Source/devtools/front_end/DebuggerScriptMapping.js
@@ -0,0 +1,66 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.Workspace} workspace
+ * @param {WebInspector.SimpleWorkspaceProvider} networkWorkspaceProvider
+ */
+WebInspector.DebuggerScriptMapping = function(workspace, networkWorkspaceProvider)
+{
+    this._defaultMapping = new WebInspector.DefaultScriptMapping(workspace);
+    this._resourceMapping = new WebInspector.ResourceScriptMapping(workspace);
+    this._compilerMapping = new WebInspector.CompilerScriptMapping(workspace, networkWorkspaceProvider);
+    this._snippetMapping = WebInspector.scriptSnippetModel.scriptMapping;
+
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._parsedScriptSource, this);
+}
+
+WebInspector.DebuggerScriptMapping.prototype = {
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _parsedScriptSource: function(event)
+    {
+        var script = /** @type {WebInspector.Script} */ (event.data);
+        this._defaultMapping.addScript(script);
+
+        if (script.isSnippet()) {
+            this._snippetMapping.addScript(script);
+            return;
+        }
+
+        this._resourceMapping.addScript(script);
+
+        if (WebInspector.settings.sourceMapsEnabled.get())
+            this._compilerMapping.addScript(script);
+    }
+}
diff --git a/Source/devtools/front_end/DefaultScriptMapping.js b/Source/devtools/front_end/DefaultScriptMapping.js
new file mode 100644
index 0000000..b1a59bc
--- /dev/null
+++ b/Source/devtools/front_end/DefaultScriptMapping.js
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.ScriptSourceMapping}
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.DefaultScriptMapping = function(workspace)
+{
+    this._projectDelegate = new WebInspector.DebuggerProjectDelegate();
+    this._workspace = workspace;
+    this._workspace.addProject(this._projectDelegate);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
+    this._debuggerReset();
+}
+
+WebInspector.DefaultScriptMapping.prototype = {
+    /**
+     * @param {WebInspector.RawLocation} rawLocation
+     * @return {WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(rawLocation)
+    {
+        var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation);
+        var script = WebInspector.debuggerModel.scriptForId(debuggerModelLocation.scriptId);
+        var uiSourceCode = this._uiSourceCodeForScriptId[script.scriptId];
+        var lineNumber = debuggerModelLocation.lineNumber;
+        var columnNumber = debuggerModelLocation.columnNumber || 0;
+        return new WebInspector.UILocation(uiSourceCode, lineNumber, columnNumber);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.DebuggerModel.Location}
+     */
+    uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
+    {
+        var scriptId = this._scriptIdForUISourceCode.get(uiSourceCode);
+        var script = WebInspector.debuggerModel.scriptForId(scriptId);
+        return WebInspector.debuggerModel.createRawLocation(script, lineNumber, columnNumber);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isIdentity: function()
+    {
+        return true;
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     */
+    addScript: function(script)
+    {
+        var path = this._projectDelegate.addScript(script);
+        var uiSourceCode = this._workspace.uiSourceCode(this._projectDelegate.id(), path);
+        this._uiSourceCodeForScriptId[script.scriptId] = uiSourceCode;
+        this._scriptIdForUISourceCode.put(uiSourceCode, script.scriptId);
+        uiSourceCode.setSourceMapping(this);
+        script.pushSourceMapping(this);
+        script.addEventListener(WebInspector.Script.Events.ScriptEdited, this._scriptEdited.bind(this, script.scriptId));
+        return uiSourceCode;
+    },
+
+    /**
+     * @param {string} scriptId
+     * @param {WebInspector.Event} event
+     */
+    _scriptEdited: function(scriptId, event)
+    {
+        var content = /** @type {string} */(event.data);
+        this._uiSourceCodeForScriptId[scriptId].addRevision(content);
+    },
+
+    _debuggerReset: function()
+    {
+        /** @type {Object.<string, WebInspector.UISourceCode>} */
+        this._uiSourceCodeForScriptId = {};
+        this._scriptIdForUISourceCode = new Map();
+        this._projectDelegate.reset();
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ContentProviderBasedProjectDelegate}
+ */
+WebInspector.DebuggerProjectDelegate = function()
+{
+    WebInspector.ContentProviderBasedProjectDelegate.call(this, WebInspector.projectTypes.Debugger);
+}
+
+WebInspector.DebuggerProjectDelegate.prototype = {
+    /**
+     * @return {string}
+     */
+    id: function()
+    {
+        return "debugger:";
+    },
+
+    /**
+     * @return {string}
+     */
+    displayName: function()
+    {
+        return "debugger";
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     * @return {Array.<string>}
+     */
+    addScript: function(script)
+    {
+        var contentProvider = script.isInlineScript() ? new WebInspector.ConcatenatedScriptsContentProvider([script]) : script;
+        var splittedURL = WebInspector.ParsedURL.splitURL(script.sourceURL);
+        var name = splittedURL[splittedURL.length - 1];
+        name = "[VM] " + name + " (" + script.scriptId + ")";
+        return this.addContentProvider([name], script.sourceURL, contentProvider, false, script.isContentScript);
+    },
+    
+    __proto__: WebInspector.ContentProviderBasedProjectDelegate.prototype
+}
diff --git a/Source/devtools/front_end/DefaultTextEditor.js b/Source/devtools/front_end/DefaultTextEditor.js
new file mode 100644
index 0000000..f33c378
--- /dev/null
+++ b/Source/devtools/front_end/DefaultTextEditor.js
@@ -0,0 +1,3771 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2010 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @implements {WebInspector.TextEditor}
+ * @param {?string} url
+ * @param {WebInspector.TextEditorDelegate} delegate
+ */
+WebInspector.DefaultTextEditor = function(url, delegate)
+{
+    WebInspector.View.call(this);
+    this._delegate = delegate;
+    this._url = url;
+
+    this.registerRequiredCSS("textEditor.css");
+
+    this.element.className = "text-editor monospace";
+
+    // Prevent middle-click pasting in the editor unless it is explicitly enabled for certain component.
+    this.element.addEventListener("mouseup", preventDefaultOnMouseUp.bind(this), false);
+    function preventDefaultOnMouseUp(event)
+    {
+        if (event.button === 1)
+            event.consume(true);
+    }
+
+    this._textModel = new WebInspector.TextEditorModel();
+    this._textModel.addEventListener(WebInspector.TextEditorModel.Events.TextChanged, this._textChanged, this);
+
+    var syncScrollListener = this._syncScroll.bind(this);
+    var syncDecorationsForLineListener = this._syncDecorationsForLine.bind(this);
+    var syncLineHeightListener = this._syncLineHeight.bind(this);
+    this._mainPanel = new WebInspector.TextEditorMainPanel(this._delegate, this._textModel, url, syncScrollListener, syncDecorationsForLineListener);
+    this._gutterPanel = new WebInspector.TextEditorGutterPanel(this._textModel, syncDecorationsForLineListener, syncLineHeightListener);
+
+    this._mainPanel.element.addEventListener("scroll", this._handleScrollChanged.bind(this), false);
+
+    this._gutterPanel.element.addEventListener("mousedown", this._onMouseDown.bind(this), true);
+
+    // Explicitly enable middle-click pasting in the editor main panel.
+    this._mainPanel.element.addEventListener("mouseup", consumeMouseUp.bind(this), false);
+    function consumeMouseUp(event)
+    {
+        if (event.button === 1)
+            event.consume(false);
+    }
+
+    this.element.appendChild(this._mainPanel.element);
+    this.element.appendChild(this._gutterPanel.element);
+
+    // Forward mouse wheel events from the unscrollable gutter to the main panel.
+    function forwardWheelEvent(event)
+    {
+        var clone = document.createEvent("WheelEvent");
+        clone.initWebKitWheelEvent(event.wheelDeltaX, event.wheelDeltaY,
+                                   event.view,
+                                   event.screenX, event.screenY,
+                                   event.clientX, event.clientY,
+                                   event.ctrlKey, event.altKey, event.shiftKey, event.metaKey);
+        this._mainPanel.element.dispatchEvent(clone);
+    }
+    this._gutterPanel.element.addEventListener("mousewheel", forwardWheelEvent.bind(this), false);
+
+    this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false);
+    this.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
+
+    this._wordMovementController = new WebInspector.DefaultTextEditor.WordMovementController(this, this._textModel);
+    this._registerShortcuts();
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextRange} range
+ * @param {string} text
+ */
+WebInspector.DefaultTextEditor.EditInfo = function(range, text)
+{
+    this.range = range;
+    this.text = text;
+}
+
+WebInspector.DefaultTextEditor.prototype = {
+
+    undo: function()
+    {
+        this._mainPanel.undo();
+    },
+
+    redo: function()
+    {
+        this._mainPanel.redo();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isClean: function()
+    {
+        return this._textModel.isClean();
+    },
+
+    markClean: function()
+    {
+        this._textModel.markClean();
+    },
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @return {?{startColumn: number, endColumn: number, type: string}}
+     */
+    tokenAtTextPosition: function(lineNumber, column)
+    {
+        return this._mainPanel.tokenAtTextPosition(lineNumber, column);
+    },
+
+    /*
+     * @param {number} lineNumber
+     * @param {number} column
+     * @return {?{x: number, y: number, height: number}}
+     */
+    cursorPositionToCoordinates: function(lineNumber, column)
+    {
+        return this._mainPanel.cursorPositionToCoordinates(lineNumber, column);
+    },
+
+    /**
+     * @param {number} x
+     * @param {number} y
+     * @return {?WebInspector.TextRange}
+     */
+    coordinatesToCursorPosition: function(x, y)
+    {
+        return this._mainPanel.coordinatesToCursorPosition(x, y);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {string}
+     */
+    copyRange: function(range)
+    {
+        return this._textModel.copyRange(range);
+    },
+
+    /**
+     * @param {string} regex
+     * @param {string} cssClass
+     * @return {Object}
+     */
+    highlightRegex: function(regex, cssClass)
+    {
+        return this._mainPanel.highlightRegex(regex, cssClass);
+    },
+
+    /**
+     * @param {Object} highlightDescriptor
+     */
+    removeHighlight: function(highlightDescriptor)
+    {
+        this._mainPanel.removeHighlight(highlightDescriptor);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} cssClass
+     * @return {Object}
+     */
+    highlightRange: function(range, cssClass)
+    {
+        return this._mainPanel.highlightRange(range, cssClass);
+    },
+
+    /**
+     * @param {string} mimeType
+     */
+    set mimeType(mimeType)
+    {
+        this._mainPanel.mimeType = mimeType;
+    },
+
+    /**
+     * @param {boolean} readOnly
+     */
+    setReadOnly: function(readOnly)
+    {
+        if (this._mainPanel.readOnly() === readOnly)
+            return;
+        this._mainPanel.setReadOnly(readOnly, this.isShowing());
+        WebInspector.markBeingEdited(this.element, !readOnly);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    readOnly: function()
+    {
+        return this._mainPanel.readOnly();
+    },
+
+    /**
+     * @return {Element}
+     */
+    defaultFocusedElement: function()
+    {
+        return this._mainPanel.defaultFocusedElement();
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    revealLine: function(lineNumber)
+    {
+        this._mainPanel.revealLine(lineNumber);
+    },
+
+    _onMouseDown: function(event)
+    {
+        var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number");
+        if (!target)
+            return;
+        this.dispatchEventToListeners(WebInspector.TextEditor.Events.GutterClick, { lineNumber: target.lineNumber, event: event });
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {boolean} disabled
+     * @param {boolean} conditional
+     */
+    addBreakpoint: function(lineNumber, disabled, conditional)
+    {
+        this.beginUpdates();
+        this._gutterPanel.addDecoration(lineNumber, "webkit-breakpoint");
+        if (disabled)
+            this._gutterPanel.addDecoration(lineNumber, "webkit-breakpoint-disabled");
+        else
+            this._gutterPanel.removeDecoration(lineNumber, "webkit-breakpoint-disabled");
+        if (conditional)
+            this._gutterPanel.addDecoration(lineNumber, "webkit-breakpoint-conditional");
+        else
+            this._gutterPanel.removeDecoration(lineNumber, "webkit-breakpoint-conditional");
+        this.endUpdates();
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    removeBreakpoint: function(lineNumber)
+    {
+        this.beginUpdates();
+        this._gutterPanel.removeDecoration(lineNumber, "webkit-breakpoint");
+        this._gutterPanel.removeDecoration(lineNumber, "webkit-breakpoint-disabled");
+        this._gutterPanel.removeDecoration(lineNumber, "webkit-breakpoint-conditional");
+        this.endUpdates();
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    setExecutionLine: function(lineNumber)
+    {
+        this._executionLineNumber = lineNumber;
+        this._mainPanel.addDecoration(lineNumber, "webkit-execution-line");
+        this._gutterPanel.addDecoration(lineNumber, "webkit-execution-line");
+    },
+
+    clearExecutionLine: function()
+    {
+        if (typeof this._executionLineNumber === "number") {
+            this._mainPanel.removeDecoration(this._executionLineNumber, "webkit-execution-line");
+            this._gutterPanel.removeDecoration(this._executionLineNumber, "webkit-execution-line");
+        }
+        delete this._executionLineNumber;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {Element} element
+     */
+    addDecoration: function(lineNumber, element)
+    {
+        this._mainPanel.addDecoration(lineNumber, element);
+        this._gutterPanel.addDecoration(lineNumber, element);
+        this._syncDecorationsForLine(lineNumber);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {Element} element
+     */
+    removeDecoration: function(lineNumber, element)
+    {
+        this._mainPanel.removeDecoration(lineNumber, element);
+        this._gutterPanel.removeDecoration(lineNumber, element);
+        this._syncDecorationsForLine(lineNumber);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     */
+    markAndRevealRange: function(range)
+    {
+        if (range)
+            this.setSelection(range);
+        this._mainPanel.markAndRevealRange(range);
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    highlightLine: function(lineNumber)
+    {
+        if (typeof lineNumber !== "number" || lineNumber < 0)
+            return;
+
+        lineNumber = Math.min(lineNumber, this._textModel.linesCount - 1);
+        this._mainPanel.highlightLine(lineNumber);
+    },
+
+    clearLineHighlight: function()
+    {
+        this._mainPanel.clearLineHighlight();
+    },
+
+    /**
+     * @return {Array.<Element>}
+     */
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return [this._mainPanel.element];
+    },
+
+    /**
+     * @param {WebInspector.TextEditor} textEditor
+     */
+    inheritScrollPositions: function(textEditor)
+    {
+        this._mainPanel.element._scrollTop = textEditor._mainPanel.element.scrollTop;
+        this._mainPanel.element._scrollLeft = textEditor._mainPanel.element.scrollLeft;
+    },
+
+    beginUpdates: function()
+    {
+        this._mainPanel.beginUpdates();
+        this._gutterPanel.beginUpdates();
+    },
+
+    endUpdates: function()
+    {
+        this._mainPanel.endUpdates();
+        this._gutterPanel.endUpdates();
+        this._updatePanelOffsets();
+    },
+
+    onResize: function()
+    {
+        this._mainPanel.resize();
+        this._gutterPanel.resize();
+        this._updatePanelOffsets();
+    },
+
+    _textChanged: function(event)
+    {
+        this._mainPanel.textChanged(event.data.oldRange, event.data.newRange);
+        this._gutterPanel.textChanged(event.data.oldRange, event.data.newRange);
+        this._updatePanelOffsets();
+        if (event.data.editRange)
+            this._delegate.onTextChanged(event.data.oldRange, event.data.newRange);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} text
+     * @return {WebInspector.TextRange}
+     */
+    editRange: function(range, text)
+    {
+        return this._textModel.editRange(range, text, this.lastSelection());
+    },
+
+    _updatePanelOffsets: function()
+    {
+        var lineNumbersWidth = this._gutterPanel.element.offsetWidth;
+        if (lineNumbersWidth)
+            this._mainPanel.element.style.setProperty("left", (lineNumbersWidth + 2) + "px");
+        else
+            this._mainPanel.element.style.removeProperty("left"); // Use default value set in CSS.
+    },
+
+    _syncScroll: function()
+    {
+        var mainElement = this._mainPanel.element;
+        var gutterElement = this._gutterPanel.element;
+        // Handle horizontal scroll bar at the bottom of the main panel.
+        this._gutterPanel.syncClientHeight(mainElement.clientHeight);
+        gutterElement.scrollTop = mainElement.scrollTop;
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    _syncDecorationsForLine: function(lineNumber)
+    {
+        if (lineNumber >= this._textModel.linesCount)
+            return;
+
+        var mainChunk = this._mainPanel.chunkForLine(lineNumber);
+        if (mainChunk.linesCount === 1 && mainChunk.isDecorated()) {
+            var gutterChunk = this._gutterPanel.makeLineAChunk(lineNumber);
+            var height = mainChunk.height;
+            if (height)
+                gutterChunk.element.style.setProperty("height", height + "px");
+            else
+                gutterChunk.element.style.removeProperty("height");
+        } else {
+            var gutterChunk = this._gutterPanel.chunkForLine(lineNumber);
+            if (gutterChunk.linesCount === 1)
+                gutterChunk.element.style.removeProperty("height");
+        }
+    },
+
+    /**
+     * @param {Element} gutterRow
+     */
+    _syncLineHeight: function(gutterRow)
+    {
+        if (this._lineHeightSynced)
+            return;
+        if (gutterRow && gutterRow.offsetHeight) {
+            // Force equal line heights for the child panels.
+            this.element.style.setProperty("line-height", gutterRow.offsetHeight + "px");
+            this._lineHeightSynced = true;
+        }
+    },
+
+    _registerShortcuts: function()
+    {
+        var keys = WebInspector.KeyboardShortcut.Keys;
+        var modifiers = WebInspector.KeyboardShortcut.Modifiers;
+
+        this._shortcuts = {};
+
+        this._shortcuts[WebInspector.KeyboardShortcut.SelectAll] = this._handleSelectAll.bind(this);
+        this._wordMovementController._registerShortcuts(this._shortcuts);
+    },
+
+    _handleSelectAll: function()
+    {
+        this.setSelection(this._textModel.range());
+        return true;
+    },
+
+    _handleKeyDown: function(e)
+    {
+        // If the event was not triggered from the entire editor, then
+        // ignore it. https://bugs.webkit.org/show_bug.cgi?id=102906
+        if (e.target.enclosingNodeOrSelfWithClass("webkit-line-decorations"))
+            return;
+
+        var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(e);
+
+        var handler = this._shortcuts[shortcutKey];
+        if (handler && handler()) {
+            e.consume(true);
+            return;
+        }
+        this._mainPanel.handleKeyDown(shortcutKey, e);
+    },
+
+    _contextMenu: function(event)
+    {
+        var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
+        if (anchor)
+            return;
+        var contextMenu = new WebInspector.ContextMenu(event);
+        var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number");
+        if (target)
+            this._delegate.populateLineGutterContextMenu(contextMenu, target.lineNumber);
+        else {
+            this._mainPanel.populateContextMenu(event.target, contextMenu);
+        }
+        contextMenu.show();
+    },
+
+    _handleScrollChanged: function(event)
+    {
+        var visibleFrom = this._mainPanel.scrollTop();
+        var firstVisibleLineNumber = this._mainPanel.lineNumberAtOffset(visibleFrom);
+        this._delegate.scrollChanged(firstVisibleLineNumber);
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollToLine: function(lineNumber)
+    {
+        this._mainPanel.scrollToLine(lineNumber);
+    },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    selection: function()
+    {
+        return this._mainPanel.selection();
+    },
+
+    /**
+     * @return {WebInspector.TextRange?}
+     */
+    lastSelection: function()
+    {
+        return this._mainPanel.lastSelection();
+    },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    setSelection: function(textRange)
+    {
+        this._mainPanel.setSelection(textRange);
+    },
+
+    /**
+     * @param {string} text
+     */
+    setText: function(text)
+    {
+        this._textModel.setText(text);
+    },
+
+    /**
+     * @return {string}
+     */
+    text: function()
+    {
+        return this._textModel.text();
+    },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    range: function()
+    {
+        return this._textModel.range();
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {string}
+     */
+    line: function(lineNumber)
+    {
+        return this._textModel.line(lineNumber);
+    },
+
+    /**
+     * @return {number}
+     */
+    get linesCount()
+    {
+        return this._textModel.linesCount;
+    },
+
+    /**
+     * @param {number} line
+     * @param {string} name
+     * @param {?Object} value
+     */
+    setAttribute: function(line, name, value)
+    {
+        this._textModel.setAttribute(line, name, value);
+    },
+
+    /**
+     * @param {number} line
+     * @param {string} name
+     * @return {?Object} value
+     */
+    getAttribute: function(line, name)
+    {
+        return this._textModel.getAttribute(line, name);
+    },
+
+    /**
+     * @param {number} line
+     * @param {string} name
+     */
+    removeAttribute: function(line, name)
+    {
+        this._textModel.removeAttribute(line, name);
+    },
+
+    wasShown: function()
+    {
+        if (!this.readOnly())
+            WebInspector.markBeingEdited(this.element, true);
+
+        this._mainPanel.wasShown();
+    },
+
+    willHide: function()
+    {
+        this._mainPanel.willHide();
+        this._gutterPanel.willHide();
+
+        if (!this.readOnly())
+            WebInspector.markBeingEdited(this.element, false);
+    },
+
+    /**
+     * @param {Element} element
+     * @param {Array.<Object>} resultRanges
+     * @param {string} styleClass
+     * @param {Array.<Object>=} changes
+     */
+    highlightRangesWithStyleClass: function(element, resultRanges, styleClass, changes)
+    {
+        this._mainPanel.beginDomUpdates();
+        WebInspector.highlightRangesWithStyleClass(element, resultRanges, styleClass, changes);
+        this._mainPanel.endDomUpdates();
+    },
+
+    /**
+     * @param {number} scrollTop
+     * @param {number} clientHeight
+     * @param {number} chunkSize
+     */
+    overrideViewportForTest: function(scrollTop, clientHeight, chunkSize)
+    {
+        this._mainPanel.overrideViewportForTest(scrollTop, clientHeight, chunkSize);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextEditorModel} textModel
+ */
+WebInspector.TextEditorChunkedPanel = function(textModel)
+{
+    this._textModel = textModel;
+
+    this.element = document.createElement("div");
+    this.element.addEventListener("scroll", this._scroll.bind(this), false);
+
+    this._defaultChunkSize = 50;
+    this._paintCoalescingLevel = 0;
+    this._domUpdateCoalescingLevel = 0;
+}
+
+WebInspector.TextEditorChunkedPanel.prototype = {
+    /**
+     * @param {number} lineNumber
+     */
+    scrollToLine: function(lineNumber)
+    {
+        if (lineNumber >= this._textModel.linesCount)
+            return;
+
+        var chunk = this.makeLineAChunk(lineNumber);
+        this.element.scrollTop = chunk.offsetTop;
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    revealLine: function(lineNumber)
+    {
+        if (lineNumber >= this._textModel.linesCount)
+            return;
+
+        var chunk = this.makeLineAChunk(lineNumber);
+        chunk.element.scrollIntoViewIfNeeded();
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string|Element} decoration
+     */
+    addDecoration: function(lineNumber, decoration)
+    {
+        if (lineNumber >= this._textModel.linesCount)
+            return;
+
+        var chunk = this.makeLineAChunk(lineNumber);
+        chunk.addDecoration(decoration);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string|Element} decoration
+     */
+    removeDecoration: function(lineNumber, decoration)
+    {
+        if (lineNumber >= this._textModel.linesCount)
+            return;
+
+        var chunk = this.chunkForLine(lineNumber);
+        chunk.removeDecoration(decoration);
+    },
+
+    buildChunks: function()
+    {
+        this.beginDomUpdates();
+
+        this._container.removeChildren();
+
+        this._textChunks = [];
+        for (var i = 0; i < this._textModel.linesCount; i += this._defaultChunkSize) {
+            var chunk = this.createNewChunk(i, i + this._defaultChunkSize);
+            this._textChunks.push(chunk);
+            this._container.appendChild(chunk.element);
+        }
+
+        this.repaintAll();
+
+        this.endDomUpdates();
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {Object}
+     */
+    makeLineAChunk: function(lineNumber)
+    {
+        var chunkNumber = this.chunkNumberForLine(lineNumber);
+        var oldChunk = this._textChunks[chunkNumber];
+
+        if (!oldChunk) {
+            console.error("No chunk for line number: " + lineNumber);
+            return null;
+        }
+
+        if (oldChunk.linesCount === 1)
+            return oldChunk;
+
+        return this.splitChunkOnALine(lineNumber, chunkNumber, true);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} chunkNumber
+     * @param {boolean=} createSuffixChunk
+     * @return {Object}
+     */
+    splitChunkOnALine: function(lineNumber, chunkNumber, createSuffixChunk)
+    {
+        this.beginDomUpdates();
+
+        var oldChunk = this._textChunks[chunkNumber];
+        var wasExpanded = oldChunk.expanded();
+        oldChunk.collapse();
+
+        var insertIndex = chunkNumber + 1;
+
+        // Prefix chunk.
+        if (lineNumber > oldChunk.startLine) {
+            var prefixChunk = this.createNewChunk(oldChunk.startLine, lineNumber);
+            this._textChunks.splice(insertIndex++, 0, prefixChunk);
+            this._container.insertBefore(prefixChunk.element, oldChunk.element);
+        }
+
+        // Line chunk.
+        var endLine = createSuffixChunk ? lineNumber + 1 : oldChunk.startLine + oldChunk.linesCount;
+        var lineChunk = this.createNewChunk(lineNumber, endLine);
+        this._textChunks.splice(insertIndex++, 0, lineChunk);
+        this._container.insertBefore(lineChunk.element, oldChunk.element);
+
+        // Suffix chunk.
+        if (oldChunk.startLine + oldChunk.linesCount > endLine) {
+            var suffixChunk = this.createNewChunk(endLine, oldChunk.startLine + oldChunk.linesCount);
+            this._textChunks.splice(insertIndex, 0, suffixChunk);
+            this._container.insertBefore(suffixChunk.element, oldChunk.element);
+        }
+
+        // Remove enclosing chunk.
+        this._textChunks.splice(chunkNumber, 1);
+        this._container.removeChild(oldChunk.element);
+
+        if (wasExpanded) {
+            if (prefixChunk)
+                prefixChunk.expand();
+            lineChunk.expand();
+            if (suffixChunk)
+                suffixChunk.expand();
+        }
+
+        this.endDomUpdates();
+
+        return lineChunk;
+    },
+
+    createNewChunk: function(startLine, endLine)
+    {
+        throw new Error("createNewChunk() should be implemented by descendants");
+    },
+
+    _scroll: function()
+    {
+        this._scheduleRepaintAll();
+        if (this._syncScrollListener)
+            this._syncScrollListener();
+    },
+
+    _scheduleRepaintAll: function()
+    {
+        if (this._repaintAllTimer)
+            clearTimeout(this._repaintAllTimer);
+        this._repaintAllTimer = setTimeout(this.repaintAll.bind(this), 50);
+    },
+
+    beginUpdates: function()
+    {
+        this._paintCoalescingLevel++;
+    },
+
+    endUpdates: function()
+    {
+        this._paintCoalescingLevel--;
+        if (!this._paintCoalescingLevel)
+            this.repaintAll();
+    },
+
+    beginDomUpdates: function()
+    {
+        this._domUpdateCoalescingLevel++;
+    },
+
+    endDomUpdates: function()
+    {
+        this._domUpdateCoalescingLevel--;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {number}
+     */
+    chunkNumberForLine: function(lineNumber)
+    {
+        function compareLineNumbers(value, chunk)
+        {
+            return value < chunk.startLine ? -1 : 1;
+        }
+        var insertBefore = insertionIndexForObjectInListSortedByFunction(lineNumber, this._textChunks, compareLineNumbers);
+        return insertBefore - 1;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {Object}
+     */
+    chunkForLine: function(lineNumber)
+    {
+        return this._textChunks[this.chunkNumberForLine(lineNumber)];
+    },
+
+    /**
+     * @param {number} visibleFrom
+     * @return {number}
+     */
+    _findFirstVisibleChunkNumber: function(visibleFrom)
+    {
+        function compareOffsetTops(value, chunk)
+        {
+            return value < chunk.offsetTop ? -1 : 1;
+        }
+        var insertBefore = insertionIndexForObjectInListSortedByFunction(visibleFrom, this._textChunks, compareOffsetTops);
+        return insertBefore - 1;
+    },
+
+    /**
+     * @param {number} visibleFrom
+     * @param {number} visibleTo
+     * @return {{start: number, end: number}}
+     */
+    findVisibleChunks: function(visibleFrom, visibleTo)
+    {
+        var span = (visibleTo - visibleFrom) * 0.5;
+        visibleFrom = Math.max(visibleFrom - span, 0);
+        visibleTo = visibleTo + span;
+
+        var from = this._findFirstVisibleChunkNumber(visibleFrom);
+        for (var to = from + 1; to < this._textChunks.length; ++to) {
+            if (this._textChunks[to].offsetTop >= visibleTo)
+                break;
+        }
+        return { start: from, end: to };
+    },
+
+    /**
+     * @param {number} visibleFrom
+     * @return {number}
+     */
+    lineNumberAtOffset: function(visibleFrom)
+    {
+        var chunk = this._textChunks[this._findFirstVisibleChunkNumber(visibleFrom)];
+        if (!chunk.expanded())
+            return chunk.startLine;
+
+        var lineNumbers = [];
+        for (var i = 0; i < chunk.linesCount; ++i) {
+            lineNumbers.push(chunk.startLine + i);
+        }
+
+        function compareLineRowOffsetTops(value, lineNumber)
+        {
+            var lineRow = chunk.expandedLineRow(lineNumber);
+            return value < lineRow.offsetTop ? -1 : 1;
+        }
+        var insertBefore = insertionIndexForObjectInListSortedByFunction(visibleFrom, lineNumbers, compareLineRowOffsetTops);
+        return lineNumbers[insertBefore - 1];
+    },
+
+    repaintAll: function()
+    {
+        delete this._repaintAllTimer;
+
+        if (this._paintCoalescingLevel)
+            return;
+
+        var visibleFrom = this.scrollTop();
+        var visibleTo = visibleFrom + this.clientHeight();
+
+        if (visibleTo) {
+            var result = this.findVisibleChunks(visibleFrom, visibleTo);
+            this.expandChunks(result.start, result.end);
+        }
+    },
+
+    scrollTop: function()
+    {
+        return typeof this._scrollTopOverrideForTest === "number" ? this._scrollTopOverrideForTest : this.element.scrollTop;
+    },
+
+    clientHeight: function()
+    {
+        return typeof this._clientHeightOverrideForTest === "number" ? this._clientHeightOverrideForTest : this.element.clientHeight;
+    },
+
+    /**
+     * @param {number} fromIndex
+     * @param {number} toIndex
+     */
+    expandChunks: function(fromIndex, toIndex)
+    {
+        // First collapse chunks to collect the DOM elements into a cache to reuse them later.
+        for (var i = 0; i < fromIndex; ++i)
+            this._textChunks[i].collapse();
+        for (var i = toIndex; i < this._textChunks.length; ++i)
+            this._textChunks[i].collapse();
+        for (var i = fromIndex; i < toIndex; ++i)
+            this._textChunks[i].expand();
+    },
+
+    /**
+     * @param {Element} firstElement
+     * @param {Element=} lastElement
+     * @return {number}
+     */
+    totalHeight: function(firstElement, lastElement)
+    {
+        lastElement = (lastElement || firstElement).nextElementSibling;
+        if (lastElement)
+            return lastElement.offsetTop - firstElement.offsetTop;
+
+        var offsetParent = firstElement.offsetParent;
+        if (offsetParent && offsetParent.scrollHeight > offsetParent.clientHeight)
+            return offsetParent.scrollHeight - firstElement.offsetTop;
+
+        var total = 0;
+        while (firstElement && firstElement !== lastElement) {
+            total += firstElement.offsetHeight;
+            firstElement = firstElement.nextElementSibling;
+        }
+        return total;
+    },
+
+    resize: function()
+    {
+        this.repaintAll();
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TextEditorChunkedPanel}
+ * @param {WebInspector.TextEditorModel} textModel
+ * @param {function(number)} syncDecorationsForLineListener
+ * @param {function(Element)} syncLineHeightListener
+ */
+WebInspector.TextEditorGutterPanel = function(textModel, syncDecorationsForLineListener, syncLineHeightListener)
+{
+    WebInspector.TextEditorChunkedPanel.call(this, textModel);
+
+    this._syncDecorationsForLineListener = syncDecorationsForLineListener;
+    this._syncLineHeightListener = syncLineHeightListener;
+
+    this.element.className = "text-editor-lines";
+
+    this._container = document.createElement("div");
+    this._container.className = "inner-container";
+    this.element.appendChild(this._container);
+
+    this._freeCachedElements();
+    this.buildChunks();
+    this._decorations = {};
+}
+
+WebInspector.TextEditorGutterPanel.prototype = {
+    _freeCachedElements: function()
+    {
+        this._cachedRows = [];
+    },
+
+    willHide: function()
+    {
+        this._freeCachedElements();
+    },
+
+    /**
+     * @param {number} startLine
+     * @param {number} endLine
+     * @return {WebInspector.TextEditorGutterChunk}
+     */
+    createNewChunk: function(startLine, endLine)
+    {
+        return new WebInspector.TextEditorGutterChunk(this, startLine, endLine);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} oldRange
+     * @param {WebInspector.TextRange} newRange
+     */
+    textChanged: function(oldRange, newRange)
+    {
+        this.beginDomUpdates();
+
+        var linesDiff = newRange.linesCount - oldRange.linesCount;
+        if (linesDiff) {
+            // Remove old chunks (if needed).
+            for (var chunkNumber = this._textChunks.length - 1; chunkNumber >= 0; --chunkNumber) {
+                var chunk = this._textChunks[chunkNumber];
+                if (chunk.startLine + chunk.linesCount <= this._textModel.linesCount)
+                    break;
+                chunk.collapse();
+                this._container.removeChild(chunk.element);
+            }
+            this._textChunks.length = chunkNumber + 1;
+
+            // Add new chunks (if needed).
+            var totalLines = 0;
+            if (this._textChunks.length) {
+                var lastChunk = this._textChunks[this._textChunks.length - 1];
+                totalLines = lastChunk.startLine + lastChunk.linesCount;
+            }
+
+            for (var i = totalLines; i < this._textModel.linesCount; i += this._defaultChunkSize) {
+                var chunk = this.createNewChunk(i, i + this._defaultChunkSize);
+                this._textChunks.push(chunk);
+                this._container.appendChild(chunk.element);
+            }
+
+            // Shift decorations if necessary
+            var decorationsToRestore = {};
+            for (var lineNumber in this._decorations) {
+                lineNumber = parseInt(lineNumber, 10);
+
+                // Do not move decorations before the start position.
+                if (lineNumber < oldRange.startLine)
+                    continue;
+                // Decorations follow the first character of line.
+                if (lineNumber === oldRange.startLine && oldRange.startColumn)
+                    continue;
+
+                var lineDecorationsCopy = this._decorations[lineNumber].slice();
+                for (var i = 0; i < lineDecorationsCopy.length; ++i)
+                    this.removeDecoration(lineNumber, lineDecorationsCopy[i]);
+                // Do not restore the decorations before the end position.
+                if (lineNumber >= oldRange.endLine)
+                    decorationsToRestore[lineNumber] = lineDecorationsCopy;
+            }
+            for (var lineNumber in decorationsToRestore) {
+                lineNumber = parseInt(lineNumber, 10);
+                var lineDecorationsCopy = decorationsToRestore[lineNumber];
+                for (var i = 0; i < lineDecorationsCopy.length; ++i)
+                    this.addDecoration(lineNumber + linesDiff, lineDecorationsCopy[i]);
+            }
+
+
+            this.repaintAll();
+        } else {
+            // Decorations may have been removed, so we may have to sync those lines.
+            var chunkNumber = this.chunkNumberForLine(newRange.startLine);
+            var chunk = this._textChunks[chunkNumber];
+            while (chunk && chunk.startLine <= newRange.endLine) {
+                if (chunk.linesCount === 1)
+                    this._syncDecorationsForLineListener(chunk.startLine);
+                chunk = this._textChunks[++chunkNumber];
+            }
+        }
+
+        this.endDomUpdates();
+    },
+
+    /**
+     * @param {number} clientHeight
+     */
+    syncClientHeight: function(clientHeight)
+    {
+        if (this.element.offsetHeight > clientHeight)
+            this._container.style.setProperty("padding-bottom", (this.element.offsetHeight - clientHeight) + "px");
+        else
+            this._container.style.removeProperty("padding-bottom");
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string|Element} decoration
+     */
+    addDecoration: function(lineNumber, decoration)
+    {
+        WebInspector.TextEditorChunkedPanel.prototype.addDecoration.call(this, lineNumber, decoration);
+        var decorations = this._decorations[lineNumber];
+        if (!decorations) {
+            decorations = [];
+            this._decorations[lineNumber] = decorations;
+        }
+        decorations.push(decoration);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string|Element} decoration
+     */
+    removeDecoration: function(lineNumber, decoration)
+    {
+        WebInspector.TextEditorChunkedPanel.prototype.removeDecoration.call(this, lineNumber, decoration);
+        var decorations = this._decorations[lineNumber];
+        if (decorations) {
+            decorations.remove(decoration);
+            if (!decorations.length)
+                delete this._decorations[lineNumber];
+        }
+    },
+
+    __proto__: WebInspector.TextEditorChunkedPanel.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextEditorGutterPanel} chunkedPanel
+ * @param {number} startLine
+ * @param {number} endLine
+ */
+WebInspector.TextEditorGutterChunk = function(chunkedPanel, startLine, endLine)
+{
+    this._chunkedPanel = chunkedPanel;
+    this._textModel = chunkedPanel._textModel;
+
+    this.startLine = startLine;
+    endLine = Math.min(this._textModel.linesCount, endLine);
+    this.linesCount = endLine - startLine;
+
+    this._expanded = false;
+
+    this.element = document.createElement("div");
+    this.element.lineNumber = startLine;
+    this.element.className = "webkit-line-number";
+
+    if (this.linesCount === 1) {
+        // Single line chunks are typically created for decorations. Host line number in
+        // the sub-element in order to allow flexible border / margin management.
+        var innerSpan = document.createElement("span");
+        innerSpan.className = "webkit-line-number-inner";
+        innerSpan.textContent = startLine + 1;
+        var outerSpan = document.createElement("div");
+        outerSpan.className = "webkit-line-number-outer";
+        outerSpan.appendChild(innerSpan);
+        this.element.appendChild(outerSpan);
+    } else {
+        var lineNumbers = [];
+        for (var i = startLine; i < endLine; ++i)
+            lineNumbers.push(i + 1);
+        this.element.textContent = lineNumbers.join("\n");
+    }
+}
+
+WebInspector.TextEditorGutterChunk.prototype = {
+    /**
+     * @param {string} decoration
+     */
+    addDecoration: function(decoration)
+    {
+        this._chunkedPanel.beginDomUpdates();
+        if (typeof decoration === "string")
+            this.element.addStyleClass(decoration);
+        this._chunkedPanel.endDomUpdates();
+    },
+
+    /**
+     * @param {string} decoration
+     */
+    removeDecoration: function(decoration)
+    {
+        this._chunkedPanel.beginDomUpdates();
+        if (typeof decoration === "string")
+            this.element.removeStyleClass(decoration);
+        this._chunkedPanel.endDomUpdates();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    expanded: function()
+    {
+        return this._expanded;
+    },
+
+    expand: function()
+    {
+        if (this.linesCount === 1)
+            this._chunkedPanel._syncDecorationsForLineListener(this.startLine);
+
+        if (this._expanded)
+            return;
+
+        this._expanded = true;
+
+        if (this.linesCount === 1)
+            return;
+
+        this._chunkedPanel.beginDomUpdates();
+
+        this._expandedLineRows = [];
+        var parentElement = this.element.parentElement;
+        for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) {
+            var lineRow = this._createRow(i);
+            parentElement.insertBefore(lineRow, this.element);
+            this._expandedLineRows.push(lineRow);
+        }
+        parentElement.removeChild(this.element);
+        this._chunkedPanel._syncLineHeightListener(this._expandedLineRows[0]);
+
+        this._chunkedPanel.endDomUpdates();
+    },
+
+    collapse: function()
+    {
+        if (this.linesCount === 1)
+            this._chunkedPanel._syncDecorationsForLineListener(this.startLine);
+
+        if (!this._expanded)
+            return;
+
+        this._expanded = false;
+
+        if (this.linesCount === 1)
+            return;
+
+        this._chunkedPanel.beginDomUpdates();
+
+        var elementInserted = false;
+        for (var i = 0; i < this._expandedLineRows.length; ++i) {
+            var lineRow = this._expandedLineRows[i];
+            var parentElement = lineRow.parentElement;
+            if (parentElement) {
+                if (!elementInserted) {
+                    elementInserted = true;
+                    parentElement.insertBefore(this.element, lineRow);
+                }
+                parentElement.removeChild(lineRow);
+            }
+            this._chunkedPanel._cachedRows.push(lineRow);
+        }
+        delete this._expandedLineRows;
+
+        this._chunkedPanel.endDomUpdates();
+    },
+
+    /**
+     * @return {number}
+     */
+    get height()
+    {
+        if (!this._expandedLineRows)
+            return this._chunkedPanel.totalHeight(this.element);
+        return this._chunkedPanel.totalHeight(this._expandedLineRows[0], this._expandedLineRows[this._expandedLineRows.length - 1]);
+    },
+
+    /**
+     * @return {number}
+     */
+    get offsetTop()
+    {
+        return (this._expandedLineRows && this._expandedLineRows.length) ? this._expandedLineRows[0].offsetTop : this.element.offsetTop;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {Element}
+     */
+    _createRow: function(lineNumber)
+    {
+        var lineRow = this._chunkedPanel._cachedRows.pop() || document.createElement("div");
+        lineRow.lineNumber = lineNumber;
+        lineRow.className = "webkit-line-number";
+        lineRow.textContent = lineNumber + 1;
+        return lineRow;
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TextEditorChunkedPanel}
+ * @param {WebInspector.TextEditorDelegate} delegate
+ * @param {WebInspector.TextEditorModel} textModel
+ * @param {?string} url
+ * @param {function()} syncScrollListener
+ * @param {function(number)} syncDecorationsForLineListener
+ */
+WebInspector.TextEditorMainPanel = function(delegate, textModel, url, syncScrollListener, syncDecorationsForLineListener)
+{
+    WebInspector.TextEditorChunkedPanel.call(this, textModel);
+
+    this._delegate = delegate;
+    this._syncScrollListener = syncScrollListener;
+    this._syncDecorationsForLineListener = syncDecorationsForLineListener;
+
+    this._url = url;
+    this._highlighter = new WebInspector.TextEditorHighlighter(textModel, this._highlightDataReady.bind(this));
+    this._readOnly = true;
+
+    this.element.className = "text-editor-contents";
+    this.element.tabIndex = 0;
+
+    this._container = document.createElement("div");
+    this._container.className = "inner-container";
+    this._container.tabIndex = 0;
+    this.element.appendChild(this._container);
+
+    this.element.addEventListener("focus", this._handleElementFocus.bind(this), false);
+    this.element.addEventListener("textInput", this._handleTextInput.bind(this), false);
+    this.element.addEventListener("cut", this._handleCut.bind(this), false);
+    this.element.addEventListener("keypress", this._handleKeyPress.bind(this), false);
+
+    this._showWhitespace = WebInspector.experimentsSettings.showWhitespaceInEditor.isEnabled();
+
+    this._container.addEventListener("focus", this._handleFocused.bind(this), false);
+
+    this._highlightDescriptors = [];
+
+    this._tokenHighlighter = new WebInspector.TextEditorMainPanel.TokenHighlighter(this, textModel);
+    this._braceMatcher = new WebInspector.TextEditorModel.BraceMatcher(textModel);
+    this._braceHighlighter = new WebInspector.TextEditorMainPanel.BraceHighlightController(this, textModel, this._braceMatcher);
+    this._smartBraceController = new WebInspector.TextEditorMainPanel.SmartBraceController(this, textModel, this._braceMatcher);
+
+    this._freeCachedElements();
+    this.buildChunks();
+    this._registerShortcuts();
+}
+
+WebInspector.TextEditorMainPanel._ConsecutiveWhitespaceChars = {
+    1: " ",
+    2: "  ",
+    4: "    ",
+    8: "        ",
+    16: "                "
+};
+
+WebInspector.TextEditorMainPanel.prototype = {
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @return {?{startColumn: number, endColumn: number, type: string}}
+     */
+    tokenAtTextPosition: function(lineNumber, column)
+    {
+        if (lineNumber >= this._textModel.linesCount || lineNumber < 0)
+            return null;
+        var line = this._textModel.line(lineNumber);
+        if (column >= line.length || column < 0)
+            return null;
+        var highlight = this._textModel.getAttribute(lineNumber, "highlight");
+        if (!highlight)
+            return this._tokenAtUnhighlightedLine(line, column);
+        function compare(value, object)
+        {
+            if (value >= object.startColumn && value <= object.endColumn)
+                return 0;
+            return value - object.startColumn;
+        }
+        var index = binarySearch(column, highlight.ranges, compare);
+        if (index >= 0) {
+            var range = highlight.ranges[index];
+            return {
+                startColumn: range.startColumn,
+                endColumn: range.endColumn,
+                type: range.token
+            };
+        }
+        return null;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @return {?{x: number, y: number, height: number}}
+     */
+    cursorPositionToCoordinates: function(lineNumber, column)
+    {
+        if (lineNumber >= this._textModel.linesCount || lineNumber < 0)
+            return null;
+        var line = this._textModel.line(lineNumber);
+        if (column > line.length || column < 0)
+            return null;
+
+        var chunk = this.chunkForLine(lineNumber);
+        if (!chunk.expanded())
+            return null;
+        var lineRow = chunk.expandedLineRow(lineNumber);
+        var ranges = [{
+            startColumn: column,
+            endColumn: column,
+            token: "measure-cursor-position"
+        }];
+        var selection = this.selection();
+
+        this.beginDomUpdates();
+        this._renderRanges(lineRow, line, ranges);
+        var spans = lineRow.getElementsByClassName("webkit-measure-cursor-position");
+        if (WebInspector.debugDefaultTextEditor)
+            console.assert(spans.length === 0);
+        var totalOffset = spans[0].totalOffset();
+        var height = spans[0].offsetHeight;
+        this._paintLineRows([lineRow]);
+        this.endDomUpdates();
+
+        this._restoreSelection(selection);
+        return {
+            x: totalOffset.left,
+            y: totalOffset.top,
+            height: height
+        };
+    },
+
+    /**
+     * @param {number} x
+     * @param {number} y
+     * @return {?WebInspector.TextRange}
+     */
+    coordinatesToCursorPosition: function(x, y)
+    {
+        var element = document.elementFromPoint(x, y);
+        if (!element)
+            return null;
+        var lineRow = element.enclosingNodeOrSelfWithClass("webkit-line-content");
+        if (!lineRow)
+            return null;
+
+        var line = this._textModel.line(lineRow.lineNumber) + " ";
+        var ranges = [];
+        const prefix = "character-position-";
+        for(var i = 0; i < line.length; ++i) {
+            ranges.push({
+                startColumn: i,
+                endColumn: i,
+                token: prefix + i
+            });
+        }
+
+        var selection = this.selection();
+
+        this.beginDomUpdates();
+        this._renderRanges(lineRow, line, ranges);
+        var charElement = document.elementFromPoint(x, y);
+        this._paintLineRows([lineRow]);
+        this.endDomUpdates();
+
+        this._restoreSelection(selection);
+        var className = charElement.className;
+        if (className.indexOf(prefix) < 0)
+            return null;
+        var column = parseInt(className.substring(className.indexOf(prefix) + prefix.length), 10);
+
+        return WebInspector.TextRange.createFromLocation(lineRow.lineNumber, column);
+    },
+
+    /**
+     * @param {string} line
+     * @param {number} column
+     * @return {?{startColumn: number, endColumn: number, type: string}}
+     */
+    _tokenAtUnhighlightedLine: function(line, column)
+    {
+        var tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer(this.mimeType);
+        tokenizer.condition = tokenizer.createInitialCondition();
+        tokenizer.line = line;
+        var lastTokenizedColumn = 0;
+        while (lastTokenizedColumn < line.length) {
+            var newColumn = tokenizer.nextToken(lastTokenizedColumn);
+            if (column < newColumn) {
+                if (!tokenizer.tokenType)
+                    return null;
+                return {
+                    startColumn: lastTokenizedColumn,
+                    endColumn: newColumn - 1,
+                    type: tokenizer.tokenType
+                };
+            } else
+                lastTokenizedColumn = newColumn;
+        }
+        return null;
+    },
+
+    _registerShortcuts: function()
+    {
+        var keys = WebInspector.KeyboardShortcut.Keys;
+        var modifiers = WebInspector.KeyboardShortcut.Modifiers;
+
+        this._shortcuts = {};
+
+        this._shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Enter.code, WebInspector.KeyboardShortcut.Modifiers.None)] = this._handleEnterKey.bind(this);
+        this._shortcuts[WebInspector.KeyboardShortcut.makeKey("z", modifiers.CtrlOrMeta)] = this._handleUndoRedo.bind(this, false);
+
+        var handleRedo = this._handleUndoRedo.bind(this, true);
+        this._shortcuts[WebInspector.KeyboardShortcut.makeKey("z", modifiers.Shift | modifiers.CtrlOrMeta)] = handleRedo;
+        if (!WebInspector.isMac())
+            this._shortcuts[WebInspector.KeyboardShortcut.makeKey("y", modifiers.CtrlOrMeta)] = handleRedo;
+
+        var handleTabKey = this._handleTabKeyPress.bind(this, false);
+        var handleShiftTabKey = this._handleTabKeyPress.bind(this, true);
+        this._shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Tab.code)] = handleTabKey;
+        this._shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Tab.code, modifiers.Shift)] = handleShiftTabKey;
+
+        var homeKey = WebInspector.isMac() ? keys.Right : keys.Home;
+        var homeModifier = WebInspector.isMac() ? modifiers.Meta : modifiers.None;
+        this._shortcuts[WebInspector.KeyboardShortcut.makeKey(homeKey.code, homeModifier)] = this._handleHomeKey.bind(this, false);
+        this._shortcuts[WebInspector.KeyboardShortcut.makeKey(homeKey.code, homeModifier | modifiers.Shift)] = this._handleHomeKey.bind(this, true);
+
+        this._charOverrides = {};
+
+        this._smartBraceController.registerShortcuts(this._shortcuts);
+        this._smartBraceController.registerCharOverrides(this._charOverrides);
+    },
+
+    _handleKeyPress: function(event)
+    {
+        if (event.target.enclosingNodeOrSelfWithClass("webkit-line-decorations"))
+            return;
+
+        var char = String.fromCharCode(event.which);
+        var handler = this._charOverrides[char];
+        if (handler && handler()) {
+            event.consume(true);
+            return;
+        }
+        this._keyDownCode = event.keyCode;
+    },
+
+    /**
+     * @param {boolean} shift
+     */
+    _handleHomeKey: function(shift)
+    {
+        var selection = this.selection();
+
+        var line = this._textModel.line(selection.endLine);
+        var firstNonBlankCharacter = 0;
+        while (firstNonBlankCharacter < line.length) {
+            var char = line.charAt(firstNonBlankCharacter);
+            if (char === " " || char === "\t")
+                ++firstNonBlankCharacter;
+            else
+                break;
+        }
+        if (firstNonBlankCharacter >= line.length || selection.endColumn === firstNonBlankCharacter)
+            return false;
+
+        selection.endColumn = firstNonBlankCharacter;
+        if (!shift)
+            selection = selection.collapseToEnd();
+        this._restoreSelection(selection);
+        return true;
+    },
+
+    /**
+     * @param {string} regex
+     * @param {string} cssClass
+     * @return {Object}
+     */
+    highlightRegex: function(regex, cssClass)
+    {
+        var highlightDescriptor = new WebInspector.TextEditorMainPanel.RegexHighlightDescriptor(new RegExp(regex, "g"), cssClass);
+        this._highlightDescriptors.push(highlightDescriptor);
+        this._repaintLineRowsAffectedByHighlightDescriptors([highlightDescriptor]);
+        return highlightDescriptor;
+    },
+
+    /**
+     * @param {Object} highlightDescriptor
+     */
+    removeHighlight: function(highlightDescriptor)
+    {
+        this._highlightDescriptors.remove(highlightDescriptor);
+        this._repaintLineRowsAffectedByHighlightDescriptors([highlightDescriptor]);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} cssClass
+     * @return {Object}
+     */
+    highlightRange: function(range, cssClass)
+    {
+        var highlightDescriptor = new WebInspector.TextEditorMainPanel.RangeHighlightDescriptor(range, cssClass);
+        this._highlightDescriptors.push(highlightDescriptor);
+        this._repaintLineRowsAffectedByHighlightDescriptors([highlightDescriptor]);
+        return highlightDescriptor;
+    },
+
+    /**
+     * @param {Array.<WebInspector.TextEditorMainPanel.HighlightDescriptor>} highlightDescriptors
+     */
+    _repaintLineRowsAffectedByHighlightDescriptors: function(highlightDescriptors)
+    {
+        var visibleFrom = this.scrollTop();
+        var visibleTo = visibleFrom + this.clientHeight();
+
+        var visibleChunks = this.findVisibleChunks(visibleFrom, visibleTo);
+
+        var affectedLineRows = [];
+        for (var i = visibleChunks.start; i < visibleChunks.end; ++i) {
+            var chunk = this._textChunks[i];
+            if (!chunk.expanded())
+                continue;
+            for (var lineNumber = chunk.startLine; lineNumber < chunk.startLine + chunk.linesCount; ++lineNumber) {
+                var lineRow = chunk.expandedLineRow(lineNumber);
+                var line = this._textModel.line(lineNumber);
+                for(var j = 0; j < highlightDescriptors.length; ++j) {
+                    if (highlightDescriptors[j].affectsLine(lineNumber, line)) {
+                        affectedLineRows.push(lineRow);
+                        break;
+                    }
+                }
+            }
+        }
+        if (affectedLineRows.length === 0)
+            return;
+        var selection = this.selection();
+        this._paintLineRows(affectedLineRows);
+        this._restoreSelection(selection);
+    },
+
+    resize: function()
+    {
+        WebInspector.TextEditorChunkedPanel.prototype.resize.call(this);
+        this._repaintLineRowsAffectedByHighlightDescriptors(this._highlightDescriptors);
+    },
+
+    wasShown: function()
+    {
+        this._boundSelectionChangeListener = this._handleSelectionChange.bind(this);
+        document.addEventListener("selectionchange", this._boundSelectionChangeListener, false);
+
+        this._isShowing = true;
+        this._attachMutationObserver();
+    },
+
+    willHide: function()
+    {
+        document.removeEventListener("selectionchange", this._boundSelectionChangeListener, false);
+        delete this._boundSelectionChangeListener;
+
+        this._detachMutationObserver();
+        this._isShowing = false;
+        this._freeCachedElements();
+    },
+
+    /**
+     * @param {Element} eventTarget
+     * @param {WebInspector.ContextMenu} contextMenu
+     */
+    populateContextMenu: function(eventTarget, contextMenu)
+    {
+        var target = this._enclosingLineRowOrSelf(eventTarget);
+        this._delegate.populateTextAreaContextMenu(contextMenu, target && target.lineNumber);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    setSelection: function(textRange)
+    {
+        this._lastSelection = textRange;
+        if (this.element.isAncestor(document.activeElement))
+            this._restoreSelection(textRange);
+    },
+
+    _handleFocused: function()
+    {
+        if (this._lastSelection)
+            this.setSelection(this._lastSelection);
+    },
+
+    _attachMutationObserver: function()
+    {
+        if (!this._isShowing)
+            return;
+
+        if (this._mutationObserver)
+            this._mutationObserver.disconnect();
+        this._mutationObserver = new NonLeakingMutationObserver(this._handleMutations.bind(this));
+        this._mutationObserver.observe(this._container, { subtree: true, childList: true, characterData: true });
+    },
+
+    _detachMutationObserver: function()
+    {
+        if (!this._isShowing)
+            return;
+
+        if (this._mutationObserver) {
+            this._mutationObserver.disconnect();
+            delete this._mutationObserver;
+        }
+    },
+
+    /**
+     * @param {string} mimeType
+     */
+    set mimeType(mimeType)
+    {
+        this._highlighter.mimeType = mimeType;
+    },
+
+    get mimeType()
+    {
+        return this._highlighter.mimeType;
+    },
+
+    /**
+     * @param {boolean} readOnly
+     * @param {boolean} requestFocus
+     */
+    setReadOnly: function(readOnly, requestFocus)
+    {
+        if (this._readOnly === readOnly)
+            return;
+
+        this.beginDomUpdates();
+        this._readOnly = readOnly;
+        if (this._readOnly)
+            this._container.removeStyleClass("text-editor-editable");
+        else {
+            this._container.addStyleClass("text-editor-editable");
+            if (requestFocus)
+                this._updateSelectionOnStartEditing();
+        }
+        this.endDomUpdates();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    readOnly: function()
+    {
+        return this._readOnly;
+    },
+
+    _handleElementFocus: function()
+    {
+        if (!this._readOnly)
+            this._container.focus();
+    },
+
+    /**
+     * @return {Element}
+     */
+    defaultFocusedElement: function()
+    {
+        if (this._readOnly)
+            return this.element;
+        return this._container;
+    },
+
+    _updateSelectionOnStartEditing: function()
+    {
+        // focus() needs to go first for the case when the last selection was inside the editor and
+        // the "Edit" button was clicked. In this case we bail at the check below, but the
+        // editor does not receive the focus, thus "Esc" does not cancel editing until at least
+        // one change has been made to the editor contents.
+        this._container.focus();
+        var selection = window.getSelection();
+        if (selection.rangeCount) {
+            var commonAncestorContainer = selection.getRangeAt(0).commonAncestorContainer;
+            if (this._container.isSelfOrAncestor(commonAncestorContainer))
+                return;
+        }
+
+        selection.removeAllRanges();
+        var range = document.createRange();
+        range.setStart(this._container, 0);
+        range.setEnd(this._container, 0);
+        selection.addRange(range);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     */
+    markAndRevealRange: function(range)
+    {
+        if (this._rangeToMark) {
+            var markedLine = this._rangeToMark.startLine;
+            delete this._rangeToMark;
+            // Remove the marked region immediately.
+            this.beginDomUpdates();
+            var chunk = this.chunkForLine(markedLine);
+            var wasExpanded = chunk.expanded();
+            chunk.collapse();
+            chunk.updateCollapsedLineRow();
+            if (wasExpanded)
+                chunk.expand();
+            this.endDomUpdates();
+        }
+
+        if (range) {
+            this._rangeToMark = range;
+            this.revealLine(range.startLine);
+            var chunk = this.makeLineAChunk(range.startLine);
+            this._paintLines(chunk.startLine, chunk.startLine + 1);
+            if (this._markedRangeElement)
+                this._markedRangeElement.scrollIntoViewIfNeeded();
+        }
+        delete this._markedRangeElement;
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    highlightLine: function(lineNumber)
+    {
+        this.clearLineHighlight();
+        this._highlightedLine = lineNumber;
+        this.revealLine(lineNumber);
+
+        if (!this._readOnly)
+            this._restoreSelection(WebInspector.TextRange.createFromLocation(lineNumber, 0), false);
+
+        this.addDecoration(lineNumber, "webkit-highlighted-line");
+    },
+
+    clearLineHighlight: function()
+    {
+        if (typeof this._highlightedLine === "number") {
+            this.removeDecoration(this._highlightedLine, "webkit-highlighted-line");
+            delete this._highlightedLine;
+        }
+    },
+
+    _freeCachedElements: function()
+    {
+        this._cachedSpans = [];
+        this._cachedTextNodes = [];
+        this._cachedRows = [];
+    },
+
+    undo: function()
+    {
+        this._handleUndoRedo(false);
+    },
+
+    redo: function()
+    {
+        this._handleUndoRedo(true);
+    },
+
+    /**
+     * @param {boolean} redo
+     * @return {boolean}
+     */
+    _handleUndoRedo: function(redo)
+    {
+        if (this.readOnly())
+            return false;
+
+        this.beginUpdates();
+
+        var range = redo ? this._textModel.redo() : this._textModel.undo();
+
+        this.endUpdates();
+
+        // Restore location post-repaint.
+        if (range)
+            this._restoreSelection(range, true);
+
+        return true;
+    },
+
+    /**
+     * @param {boolean} shiftKey
+     * @return {boolean}
+     */
+    _handleTabKeyPress: function(shiftKey)
+    {
+        if (this.readOnly())
+            return false;
+
+        var selection = this.selection();
+        if (!selection)
+            return false;
+
+        var range = selection.normalize();
+
+        this.beginUpdates();
+
+        var newRange;
+        var rangeWasEmpty = range.isEmpty();
+        if (shiftKey)
+            newRange = this._textModel.unindentLines(range);
+        else {
+            if (rangeWasEmpty)
+                newRange = this._textModel.editRange(range, WebInspector.settings.textEditorIndent.get());
+            else
+                newRange = this._textModel.indentLines(range);
+        }
+
+        this.endUpdates();
+        if (rangeWasEmpty)
+            newRange.startColumn = newRange.endColumn;
+        this._restoreSelection(newRange, true);
+        return true;
+    },
+
+    _handleEnterKey: function()
+    {
+        if (this.readOnly())
+            return false;
+
+        var range = this.selection();
+        if (!range)
+            return false;
+
+        range = range.normalize();
+
+        if (range.endColumn === 0)
+            return false;
+
+        var line = this._textModel.line(range.startLine);
+        var linePrefix = line.substring(0, range.startColumn);
+        var indentMatch = linePrefix.match(/^\s+/);
+        var currentIndent = indentMatch ? indentMatch[0] : "";
+
+        var textEditorIndent = WebInspector.settings.textEditorIndent.get();
+        var indent = WebInspector.TextEditorModel.endsWithBracketRegex.test(linePrefix) ? currentIndent + textEditorIndent : currentIndent;
+
+        if (!indent)
+            return false;
+
+        this.beginDomUpdates();
+
+        var lineBreak = this._textModel.lineBreak;
+        var newRange;
+        if (range.isEmpty() && line.substr(range.endColumn - 1, 2) === '{}') {
+            // {|}
+            // becomes
+            // {
+            //     |
+            // }
+            newRange = this._textModel.editRange(range, lineBreak + indent + lineBreak + currentIndent);
+            newRange.endLine--;
+            newRange.endColumn += textEditorIndent.length;
+        } else
+            newRange = this._textModel.editRange(range, lineBreak + indent);
+
+        this.endDomUpdates();
+        this._restoreSelection(newRange.collapseToEnd(), true);
+
+        return true;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} chunkNumber
+     * @param {boolean=} createSuffixChunk
+     * @return {Object}
+     */
+    splitChunkOnALine: function(lineNumber, chunkNumber, createSuffixChunk)
+    {
+        var selection = this.selection();
+        var chunk = WebInspector.TextEditorChunkedPanel.prototype.splitChunkOnALine.call(this, lineNumber, chunkNumber, createSuffixChunk);
+        this._restoreSelection(selection);
+        return chunk;
+    },
+
+    beginDomUpdates: function()
+    {
+        if (!this._domUpdateCoalescingLevel)
+            this._detachMutationObserver();
+        WebInspector.TextEditorChunkedPanel.prototype.beginDomUpdates.call(this);
+    },
+
+    endDomUpdates: function()
+    {
+        WebInspector.TextEditorChunkedPanel.prototype.endDomUpdates.call(this);
+        if (!this._domUpdateCoalescingLevel)
+            this._attachMutationObserver();
+    },
+
+    buildChunks: function()
+    {
+        for (var i = 0; i < this._textModel.linesCount; ++i)
+            this._textModel.removeAttribute(i, "highlight");
+
+        WebInspector.TextEditorChunkedPanel.prototype.buildChunks.call(this);
+    },
+
+    /**
+     * @param {number} startLine
+     * @param {number} endLine
+     * @return {WebInspector.TextEditorMainChunk}
+     */
+    createNewChunk: function(startLine, endLine)
+    {
+        return new WebInspector.TextEditorMainChunk(this, startLine, endLine);
+    },
+
+    /**
+     * @param {number} fromIndex
+     * @param {number} toIndex
+     */
+    expandChunks: function(fromIndex, toIndex)
+    {
+        var lastChunk = this._textChunks[toIndex - 1];
+        var lastVisibleLine = lastChunk.startLine + lastChunk.linesCount;
+
+        var selection = this.selection();
+
+        this._muteHighlightListener = true;
+        this._highlighter.highlight(lastVisibleLine);
+        delete this._muteHighlightListener;
+
+        WebInspector.TextEditorChunkedPanel.prototype.expandChunks.call(this, fromIndex, toIndex);
+
+        this._restoreSelection(selection);
+    },
+
+    /**
+     * @param {number} fromLine
+     * @param {number} toLine
+     */
+    _highlightDataReady: function(fromLine, toLine)
+    {
+        if (this._muteHighlightListener)
+            return;
+        this._paintLines(fromLine, toLine, true /*restoreSelection*/);
+    },
+
+    /**
+     * @param {number} fromLine
+     * @param {number} toLine
+     * @param {boolean=} restoreSelection
+     */
+    _paintLines: function(fromLine, toLine, restoreSelection)
+    {
+        var lineRows = [];
+        var chunk;
+        for (var lineNumber = fromLine; lineNumber < toLine; ++lineNumber) {
+            if (!chunk || lineNumber < chunk.startLine || lineNumber >= chunk.startLine + chunk.linesCount)
+                chunk = this.chunkForLine(lineNumber);
+            var lineRow = chunk.expandedLineRow(lineNumber);
+            if (!lineRow)
+                continue;
+            lineRows.push(lineRow);
+        }
+        if (lineRows.length === 0)
+            return;
+
+        var selection;
+        if (restoreSelection)
+            selection = this.selection();
+
+        this._paintLineRows(lineRows);
+
+        if (restoreSelection)
+            this._restoreSelection(selection);
+    },
+
+    /**
+     * @param {Array.<Element>} lineRows
+     */
+    _paintLineRows: function(lineRows)
+    {
+        var highlight = {};
+        this.beginDomUpdates();
+        for(var i = 0; i < this._highlightDescriptors.length; ++i) {
+            var highlightDescriptor = this._highlightDescriptors[i];
+            this._measureHighlightDescriptor(highlight, lineRows, highlightDescriptor);
+        }
+
+        for(var i = 0; i < lineRows.length; ++i)
+            this._paintLine(lineRows[i], highlight[lineRows[i].lineNumber]);
+
+        this.endDomUpdates();
+    },
+
+    /**
+     * @param {Object.<number, Array.<WebInspector.TextEditorMainPanel.LineOverlayHighlight>>} highlight
+     * @param {Array.<Element>} lineRows
+     * @param {WebInspector.TextEditorMainPanel.HighlightDescriptor} highlightDescriptor
+     */
+    _measureHighlightDescriptor: function(highlight, lineRows, highlightDescriptor)
+    {
+        var rowsToMeasure = [];
+        for(var i = 0; i < lineRows.length; ++i) {
+            var lineRow = lineRows[i];
+            var line = this._textModel.line(lineRow.lineNumber);
+            var ranges = highlightDescriptor.rangesForLine(lineRow.lineNumber, line);
+            if (ranges.length === 0)
+                continue;
+            for(var j = 0; j < ranges.length; ++j)
+                ranges[j].token = "measure-span";
+
+            this._renderRanges(lineRow, line, ranges);
+            rowsToMeasure.push(lineRow);
+        }
+
+        for(var i = 0; i < rowsToMeasure.length; ++i) {
+            var lineRow = rowsToMeasure[i];
+            var lineNumber = lineRow.lineNumber;
+            var metrics = this._measureSpans(lineRow);
+
+            if (!highlight[lineNumber])
+                highlight[lineNumber] = [];
+
+            highlight[lineNumber].push(new WebInspector.TextEditorMainPanel.LineOverlayHighlight(metrics, highlightDescriptor.cssClass()));
+        }
+    },
+
+    /**
+     * @param {Element} lineRow
+     * @return {Array.<WebInspector.TextEditorMainPanel.ElementMetrics>}
+     */
+    _measureSpans: function(lineRow)
+    {
+        var spans = lineRow.getElementsByClassName("webkit-measure-span");
+        var metrics = [];
+        for(var i = 0; i < spans.length; ++i)
+            metrics.push(new WebInspector.TextEditorMainPanel.ElementMetrics(spans[i]));
+        return metrics;
+    },
+
+    /**
+     * @param {Element} lineRow
+     * @param {WebInspector.TextEditorMainPanel.LineOverlayHighlight} highlight
+     */
+    _appendOverlayHighlight: function(lineRow, highlight)
+    {
+        var metrics = highlight.metrics;
+        var cssClass = highlight.cssClass;
+        for(var i = 0; i < metrics.length; ++i) {
+            var highlightSpan = document.createElement("span");
+            highlightSpan._isOverlayHighlightElement = true;
+            highlightSpan.addStyleClass(cssClass);
+            highlightSpan.style.left = metrics[i].left + "px";
+            highlightSpan.style.width = metrics[i].width + "px";
+            highlightSpan.style.height = metrics[i].height + "px";
+            highlightSpan.addStyleClass("text-editor-overlay-highlight");
+            lineRow.insertBefore(highlightSpan, lineRow.decorationsElement);
+        }
+    },
+
+    /**
+     * @param {Element} lineRow
+     * @param {string} line
+     * @param {Array.<{startColumn: number, endColumn: number, token: ?string}>} ranges
+     * @param {boolean=} splitWhitespaceSequences
+     */
+    _renderRanges: function(lineRow, line, ranges, splitWhitespaceSequences)
+    {
+        var decorationsElement = lineRow.decorationsElement;
+
+        if (!decorationsElement)
+            lineRow.removeChildren();
+        else {
+            while (true) {
+                var child = lineRow.firstChild;
+                if (!child || child === decorationsElement)
+                    break;
+                lineRow.removeChild(child);
+            }
+        }
+
+        if (!line)
+            lineRow.insertBefore(document.createElement("br"), decorationsElement);
+
+        var plainTextStart = 0;
+        for(var i = 0; i < ranges.length; i++) {
+            var rangeStart = ranges[i].startColumn;
+            var rangeEnd = ranges[i].endColumn;
+
+            if (plainTextStart < rangeStart) {
+                this._insertSpanBefore(lineRow, decorationsElement, line.substring(plainTextStart, rangeStart));
+            }
+
+            if (splitWhitespaceSequences && ranges[i].token === "whitespace")
+                this._renderWhitespaceCharsWithFixedSizeSpans(lineRow, decorationsElement, rangeEnd - rangeStart + 1);
+            else
+                this._insertSpanBefore(lineRow, decorationsElement, line.substring(rangeStart, rangeEnd + 1), ranges[i].token ? "webkit-" + ranges[i].token : "");
+            plainTextStart = rangeEnd + 1;
+        }
+        if (plainTextStart < line.length) {
+            this._insertSpanBefore(lineRow, decorationsElement, line.substring(plainTextStart, line.length));
+        }
+    },
+
+    /**
+     * @param {Element} lineRow
+     * @param {Element} decorationsElement
+     * @param {number} length
+     */
+    _renderWhitespaceCharsWithFixedSizeSpans: function(lineRow, decorationsElement, length)
+    {
+        for (var whitespaceLength = 16; whitespaceLength > 0; whitespaceLength >>= 1) {
+            var cssClass = "webkit-whitespace webkit-whitespace-" + whitespaceLength;
+            for (; length >= whitespaceLength; length -= whitespaceLength)
+                this._insertSpanBefore(lineRow, decorationsElement, WebInspector.TextEditorMainPanel._ConsecutiveWhitespaceChars[whitespaceLength], cssClass);
+        }
+    },
+
+    /**
+     * @param {Element} lineRow
+     * @param {Array.<WebInspector.TextEditorMainPanel.LineOverlayHighlight>} overlayHighlight
+     */
+    _paintLine: function(lineRow, overlayHighlight)
+    {
+        var lineNumber = lineRow.lineNumber;
+
+        this.beginDomUpdates();
+        try {
+            var syntaxHighlight = this._textModel.getAttribute(lineNumber, "highlight");
+
+            var line = this._textModel.line(lineNumber);
+            var ranges = syntaxHighlight ? syntaxHighlight.ranges : [];
+            this._renderRanges(lineRow, line, ranges, this._showWhitespace);
+
+            if (overlayHighlight)
+                for(var i = 0; i < overlayHighlight.length; ++i)
+                    this._appendOverlayHighlight(lineRow, overlayHighlight[i]);
+        } finally {
+            if (this._rangeToMark && this._rangeToMark.startLine === lineNumber)
+                this._markedRangeElement = WebInspector.highlightSearchResult(lineRow, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn);
+            this.endDomUpdates();
+        }
+    },
+
+    /**
+     * @param {Element} lineRow
+     */
+    _releaseLinesHighlight: function(lineRow)
+    {
+        if (!lineRow)
+            return;
+        if ("spans" in lineRow) {
+            var spans = lineRow.spans;
+            for (var j = 0; j < spans.length; ++j)
+                this._cachedSpans.push(spans[j]);
+            delete lineRow.spans;
+        }
+        if ("textNodes" in lineRow) {
+            var textNodes = lineRow.textNodes;
+            for (var j = 0; j < textNodes.length; ++j)
+                this._cachedTextNodes.push(textNodes[j]);
+            delete lineRow.textNodes;
+        }
+        this._cachedRows.push(lineRow);
+    },
+
+    /**
+     * @param {?Node=} lastUndamagedLineRow
+     * @return {WebInspector.TextRange}
+     */
+    selection: function(lastUndamagedLineRow)
+    {
+        var selection = window.getSelection();
+        if (!selection.rangeCount)
+            return null;
+        // Selection may be outside of the editor.
+        if (!this._container.isAncestor(selection.anchorNode) || !this._container.isAncestor(selection.focusNode))
+            return null;
+        // Selection may be inside one of decorations.
+        if (selection.focusNode.enclosingNodeOrSelfWithClass("webkit-line-decorations", this._container))
+            return null;
+        var start = this._selectionToPosition(selection.anchorNode, selection.anchorOffset, lastUndamagedLineRow);
+        var end = selection.isCollapsed ? start : this._selectionToPosition(selection.focusNode, selection.focusOffset, lastUndamagedLineRow);
+        return new WebInspector.TextRange(start.line, start.column, end.line, end.column);
+    },
+
+    lastSelection: function()
+    {
+        return this._lastSelection;
+    },
+
+    /**
+     * @param {boolean=} scrollIntoView
+     */
+    _restoreSelection: function(range, scrollIntoView)
+    {
+        if (!range)
+            return;
+
+        var start = this._positionToSelection(range.startLine, range.startColumn);
+        var end = range.isEmpty() ? start : this._positionToSelection(range.endLine, range.endColumn);
+        window.getSelection().setBaseAndExtent(start.container, start.offset, end.container, end.offset);
+
+        if (scrollIntoView) {
+            for (var node = end.container; node; node = node.parentElement) {
+                if (node.scrollIntoViewIfNeeded) {
+                    node.scrollIntoViewIfNeeded();
+                    break;
+                }
+            }
+        }
+        this._lastSelection = range;
+    },
+
+    /**
+     * @param {Node} container
+     * @param {number} offset
+     * @param {?Node=} lastUndamagedLineRow
+     * @return {{line: number, column: number}}
+     */
+    _selectionToPosition: function(container, offset, lastUndamagedLineRow)
+    {
+        if (container === this._container && offset === 0)
+            return { line: 0, column: 0 };
+        if (container === this._container && offset === 1)
+            return { line: this._textModel.linesCount - 1, column: this._textModel.lineLength(this._textModel.linesCount - 1) };
+
+        // This method can be called on the damaged DOM (when DOM does not match model).
+        // We need to start counting lines from the first undamaged line if it is given.
+        var lineNumber;
+        var column = 0;
+        var node;
+        var scopeNode;
+        if (lastUndamagedLineRow === null) {
+             // Last undamaged row is given, but is null - force traverse from the beginning
+            node = this._container.firstChild;
+            scopeNode = this._container;
+            lineNumber = 0;
+        } else {
+            var lineRow = this._enclosingLineRowOrSelf(container);
+            if (!lastUndamagedLineRow || (typeof lineRow.lineNumber === "number" && lineRow.lineNumber <= lastUndamagedLineRow.lineNumber)) {
+                // DOM is consistent (or we belong to the first damaged row)- lookup the row we belong to and start with it.
+                node = lineRow;
+                scopeNode = node;
+                lineNumber = node.lineNumber;
+            } else {
+                // Start with the node following undamaged row. It corresponds to lineNumber + 1.
+                node = lastUndamagedLineRow.nextSibling;
+                scopeNode = this._container;
+                lineNumber = lastUndamagedLineRow.lineNumber + 1;
+            }
+        }
+
+        // Fast return the line start.
+        if (container === node && offset === 0)
+            return { line: lineNumber, column: 0 };
+
+        // Traverse text and increment lineNumber / column.
+        for (; node && node !== container; node = node.traverseNextNode(scopeNode)) {
+            if (node.nodeName.toLowerCase() === "br") {
+                lineNumber++;
+                column = 0;
+            } else if (node.nodeType === Node.TEXT_NODE) {
+                var text = node.textContent;
+                for (var i = 0; i < text.length; ++i) {
+                    if (text.charAt(i) === "\n") {
+                        lineNumber++;
+                        column = 0;
+                    } else
+                        column++;
+                }
+            }
+        }
+
+        // We reached our container node, traverse within itself until we reach given offset.
+        if (node === container && offset) {
+            var text = node.textContent;
+            // In case offset == 1 and lineRow is a chunk div, we need to traverse it all.
+            var textOffset = (node._chunk && offset === 1) ? text.length : offset;
+            for (var i = 0; i < textOffset; ++i) {
+                if (text.charAt(i) === "\n") {
+                    lineNumber++;
+                    column = 0;
+                } else
+                    column++;
+            }
+        }
+        return { line: lineNumber, column: column };
+    },
+
+    /**
+     * @param {number} line
+     * @param {number} column
+     * @return {{container: Element, offset: number}}
+     */
+    _positionToSelection: function(line, column)
+    {
+        var chunk = this.chunkForLine(line);
+        // One-lined collapsed chunks may still stay highlighted.
+        var lineRow = chunk.linesCount === 1 ? chunk.element : chunk.expandedLineRow(line);
+        if (lineRow)
+            var rangeBoundary = lineRow.rangeBoundaryForOffset(column);
+        else {
+            var offset = column;
+            for (var i = chunk.startLine; i < line && i < this._textModel.linesCount; ++i)
+                offset += this._textModel.lineLength(i) + 1; // \n
+            lineRow = chunk.element;
+            if (lineRow.firstChild)
+                var rangeBoundary = { container: lineRow.firstChild, offset: offset };
+            else
+                var rangeBoundary = { container: lineRow, offset: 0 };
+        }
+        return rangeBoundary;
+    },
+
+    /**
+     * @param {Node} element
+     * @return {?Node}
+     */
+    _enclosingLineRowOrSelf: function(element)
+    {
+        var lineRow = element.enclosingNodeOrSelfWithClass("webkit-line-content");
+        if (lineRow)
+            return lineRow;
+
+        for (lineRow = element; lineRow; lineRow = lineRow.parentElement) {
+            if (lineRow.parentElement === this._container)
+                return lineRow;
+        }
+        return null;
+    },
+
+    /**
+     * @param {Element} element
+     * @param {Element} oldChild
+     * @param {string} content
+     * @param {string=} className
+     */
+    _insertSpanBefore: function(element, oldChild, content, className)
+    {
+        if (className === "html-resource-link" || className === "html-external-link") {
+            element.insertBefore(this._createLink(content, className === "html-external-link"), oldChild);
+            return;
+        }
+
+        var span = this._cachedSpans.pop() || document.createElement("span");
+        if (!className)
+            span.removeAttribute("class");
+        else
+            span.className = className;
+        if (WebInspector.FALSE) // For paint debugging.
+            span.addStyleClass("debug-fadeout");
+        span.textContent = content;
+        element.insertBefore(span, oldChild);
+        if (!("spans" in element))
+            element.spans = [];
+        element.spans.push(span);
+    },
+
+    /**
+     * @param {Element} element
+     * @param {Element} oldChild
+     * @param {string} text
+     */
+    _insertTextNodeBefore: function(element, oldChild, text)
+    {
+        var textNode = this._cachedTextNodes.pop();
+        if (textNode)
+            textNode.nodeValue = text;
+        else
+            textNode = document.createTextNode(text);
+        element.insertBefore(textNode, oldChild);
+        if (!("textNodes" in element))
+            element.textNodes = [];
+        element.textNodes.push(textNode);
+    },
+
+    /**
+     * @param {string} content
+     * @param {boolean} isExternal
+     * @return {Element}
+     */
+    _createLink: function(content, isExternal)
+    {
+        var quote = content.charAt(0);
+        if (content.length > 1 && (quote === "\"" || quote === "'"))
+            content = content.substring(1, content.length - 1);
+        else
+            quote = null;
+
+        var span = document.createElement("span");
+        span.className = "webkit-html-attribute-value";
+        if (quote)
+            span.appendChild(document.createTextNode(quote));
+        span.appendChild(this._delegate.createLink(content, isExternal));
+        if (quote)
+            span.appendChild(document.createTextNode(quote));
+        return span;
+    },
+
+    /**
+     * @param {Array.<WebKitMutation>} mutations
+     */
+    _handleMutations: function(mutations)
+    {
+        if (this._readOnly) {
+            delete this._keyDownCode;
+            return;
+        }
+
+        // Annihilate noop BR addition + removal that takes place upon line removal.
+        var filteredMutations = mutations.slice();
+        var addedBRs = new Map();
+        for (var i = 0; i < mutations.length; ++i) {
+            var mutation = mutations[i];
+            if (mutation.type !== "childList")
+                continue;
+            if (mutation.addedNodes.length === 1 && mutation.addedNodes[0].nodeName === "BR")
+                addedBRs.put(mutation.addedNodes[0], mutation);
+            else if (mutation.removedNodes.length === 1 && mutation.removedNodes[0].nodeName === "BR") {
+                var noopMutation = addedBRs.get(mutation.removedNodes[0]);
+                if (noopMutation) {
+                    filteredMutations.remove(mutation);
+                    filteredMutations.remove(noopMutation);
+                }
+            }
+        }
+
+        var dirtyLines;
+        for (var i = 0; i < filteredMutations.length; ++i) {
+            var mutation = filteredMutations[i];
+            var changedNodes = [];
+            if (mutation.type === "childList" && mutation.addedNodes.length)
+                changedNodes = Array.prototype.slice.call(mutation.addedNodes);
+            else if (mutation.type === "childList" && mutation.removedNodes.length)
+                changedNodes = Array.prototype.slice.call(mutation.removedNodes);
+            changedNodes.push(mutation.target);
+
+            for (var j = 0; j < changedNodes.length; ++j) {
+                var lines = this._collectDirtyLines(mutation, changedNodes[j]);
+                if (!lines)
+                    continue;
+                if (!dirtyLines) {
+                    dirtyLines = lines;
+                    continue;
+                }
+                dirtyLines.start = Math.min(dirtyLines.start, lines.start);
+                dirtyLines.end = Math.max(dirtyLines.end, lines.end);
+            }
+        }
+        if (dirtyLines) {
+            delete this._rangeToMark;
+            this._applyDomUpdates(dirtyLines);
+        }
+
+        this._assertDOMMatchesTextModel();
+
+        delete this._keyDownCode;
+    },
+
+    /**
+     * @param {WebKitMutation} mutation
+     * @param {Node} target
+     * @return {?Object}
+     */
+    _collectDirtyLines: function(mutation, target)
+    {
+        var lineRow = this._enclosingLineRowOrSelf(target);
+        if (!lineRow)
+            return null;
+
+        if (lineRow.decorationsElement && lineRow.decorationsElement.isSelfOrAncestor(target)) {
+            if (this._syncDecorationsForLineListener)
+                this._syncDecorationsForLineListener(lineRow.lineNumber);
+            return null;
+        }
+
+        if (typeof lineRow.lineNumber !== "number")
+            return null;
+
+        var startLine = lineRow.lineNumber;
+        var endLine = lineRow._chunk ? lineRow._chunk.endLine - 1 : lineRow.lineNumber;
+        return { start: startLine, end: endLine };
+    },
+
+    /**
+     * @param {Object} dirtyLines
+     */
+    _applyDomUpdates: function(dirtyLines)
+    {
+        var lastUndamagedLineNumber = dirtyLines.start - 1; // Can be -1
+        var firstUndamagedLineNumber = dirtyLines.end + 1; // Can be this._textModel.linesCount
+
+        var lastUndamagedLineChunk = lastUndamagedLineNumber >= 0 ? this._textChunks[this.chunkNumberForLine(lastUndamagedLineNumber)] : null;
+        var firstUndamagedLineChunk = firstUndamagedLineNumber < this._textModel.linesCount ? this._textChunks[this.chunkNumberForLine(firstUndamagedLineNumber)] : null;
+
+        var collectLinesFromNode = lastUndamagedLineChunk ? lastUndamagedLineChunk.lineRowContainingLine(lastUndamagedLineNumber) : null;
+        var collectLinesToNode = firstUndamagedLineChunk ? firstUndamagedLineChunk.lineRowContainingLine(firstUndamagedLineNumber) : null;
+        var lines = this._collectLinesFromDOM(collectLinesFromNode, collectLinesToNode);
+
+        var startLine = dirtyLines.start;
+        var endLine = dirtyLines.end;
+
+        var originalSelection = this._lastSelection;
+        var editInfo = this._guessEditRangeBasedOnSelection(startLine, endLine, lines);
+        if (!editInfo) {
+            if (WebInspector.debugDefaultTextEditor)
+                console.warn("Falling back to expensive edit");
+            var range = new WebInspector.TextRange(startLine, 0, endLine, this._textModel.lineLength(endLine));
+            if (!lines.length) {
+                // Entire damaged area has collapsed. Replace everything between start and end lines with nothing.
+                editInfo = new WebInspector.DefaultTextEditor.EditInfo(this._textModel.growRangeRight(range), "");
+            } else
+                editInfo = new WebInspector.DefaultTextEditor.EditInfo(range, lines.join("\n"));
+        }
+
+        var selection = this.selection(collectLinesFromNode);
+
+        // Unindent after block
+        if (editInfo.text === "}" && editInfo.range.isEmpty() && selection.isEmpty() && !this._textModel.line(editInfo.range.endLine).trim()) {
+            var offset = this._closingBlockOffset(editInfo.range);
+            if (offset >= 0) {
+                editInfo.range.startColumn = offset;
+                selection.startColumn = offset + 1;
+                selection.endColumn = offset + 1;
+            }
+        }
+
+        this._textModel.editRange(editInfo.range, editInfo.text, originalSelection);
+        this._restoreSelection(selection);
+    },
+
+    /**
+     * @param {number} startLine
+     * @param {number} endLine
+     * @param {Array.<string>} lines
+     * @return {?WebInspector.DefaultTextEditor.EditInfo}
+     */
+    _guessEditRangeBasedOnSelection: function(startLine, endLine, lines)
+    {
+        // Analyze input data
+        var textInputData = this._textInputData;
+        delete this._textInputData;
+        var isBackspace = this._keyDownCode === WebInspector.KeyboardShortcut.Keys.Backspace.code;
+        var isDelete = this._keyDownCode === WebInspector.KeyboardShortcut.Keys.Delete.code;
+
+        if (!textInputData && (isDelete || isBackspace))
+            textInputData = "";
+
+        // Return if there is no input data or selection
+        if (typeof textInputData === "undefined" || !this._lastSelection)
+            return null;
+
+        // Adjust selection based on the keyboard actions (grow for backspace, etc.).
+        textInputData = textInputData || "";
+        var range = this._lastSelection.normalize();
+        if (isBackspace && range.isEmpty())
+            range = this._textModel.growRangeLeft(range);
+        else if (isDelete && range.isEmpty())
+            range = this._textModel.growRangeRight(range);
+
+        // Test that selection intersects damaged lines
+        if (startLine > range.endLine || endLine < range.startLine)
+            return null;
+
+        var replacementLineCount = textInputData.split("\n").length - 1;
+        var lineCountDelta = replacementLineCount - range.linesCount;
+        if (startLine + lines.length - endLine - 1 !== lineCountDelta)
+            return null;
+
+        // Clone text model of the size that fits both: selection before edit and the damaged lines after edit.
+        var cloneFromLine = Math.min(range.startLine, startLine);
+        var postLastLine = startLine + lines.length + lineCountDelta;
+        var cloneToLine = Math.min(Math.max(postLastLine, range.endLine) + 1, this._textModel.linesCount);
+        var domModel = this._textModel.slice(cloneFromLine, cloneToLine);
+        domModel.editRange(range.shift(-cloneFromLine), textInputData);
+
+        // Then we'll test if this new model matches the DOM lines.
+        for (var i = 0; i < lines.length; ++i) {
+            if (domModel.line(i + startLine - cloneFromLine) !== lines[i])
+                return null;
+        }
+        return new WebInspector.DefaultTextEditor.EditInfo(range, textInputData);
+    },
+
+    _assertDOMMatchesTextModel: function()
+    {
+        if (!WebInspector.debugDefaultTextEditor)
+            return;
+
+        console.assert(this.element.innerText === this._textModel.text() + "\n", "DOM does not match model.");
+        for (var lineRow = this._container.firstChild; lineRow; lineRow = lineRow.nextSibling) {
+            var lineNumber = lineRow.lineNumber;
+            if (typeof lineNumber !== "number") {
+                console.warn("No line number on line row");
+                continue;
+            }
+            if (lineRow._chunk) {
+                var chunk = lineRow._chunk;
+                console.assert(lineNumber === chunk.startLine);
+                var chunkText = this._textModel.copyRange(new WebInspector.TextRange(chunk.startLine, 0, chunk.endLine - 1, this._textModel.lineLength(chunk.endLine - 1)));
+                if (chunkText !== lineRow.textContent)
+                    console.warn("Chunk is not matching: %d %O", lineNumber, lineRow);
+            } else if (this._textModel.line(lineNumber) !== lineRow.textContent)
+                console.warn("Line is not matching: %d %O", lineNumber, lineRow);
+        }
+    },
+
+    /**
+     * @param {WebInspector.TextRange} oldRange
+     * @return {number}
+     */
+    _closingBlockOffset: function(oldRange)
+    {
+        var leftBrace = this._braceMatcher.findLeftCandidate(oldRange.startLine, oldRange.startColumn);
+        if (!leftBrace || leftBrace.token !== "block-start")
+            return -1;
+        var lineContent = this._textModel.line(leftBrace.lineNumber);
+        return lineContent.length - lineContent.trimLeft().length;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} oldRange
+     * @param {WebInspector.TextRange} newRange
+     */
+    textChanged: function(oldRange, newRange)
+    {
+        this.beginDomUpdates();
+        this._removeDecorationsInRange(oldRange);
+        this._updateChunksForRanges(oldRange, newRange);
+        this._updateHighlightsForRange(newRange);
+        this.endDomUpdates();
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     */
+    _removeDecorationsInRange: function(range)
+    {
+        for (var i = this.chunkNumberForLine(range.startLine); i < this._textChunks.length; ++i) {
+            var chunk = this._textChunks[i];
+            if (chunk.startLine > range.endLine)
+                break;
+            chunk.removeAllDecorations();
+        }
+    },
+
+    /**
+     * @param {WebInspector.TextRange} oldRange
+     * @param {WebInspector.TextRange} newRange
+     */
+    _updateChunksForRanges: function(oldRange, newRange)
+    {
+        var firstDamagedChunkNumber = this.chunkNumberForLine(oldRange.startLine);
+        var lastDamagedChunkNumber = firstDamagedChunkNumber;
+        while (lastDamagedChunkNumber + 1 < this._textChunks.length) {
+            if (this._textChunks[lastDamagedChunkNumber + 1].startLine > oldRange.endLine)
+                break;
+            ++lastDamagedChunkNumber;
+        }
+
+        var firstDamagedChunk = this._textChunks[firstDamagedChunkNumber];
+        var lastDamagedChunk = this._textChunks[lastDamagedChunkNumber];
+
+        var linesDiff = newRange.linesCount - oldRange.linesCount;
+
+        // First, detect chunks that have not been modified and simply shift them.
+        if (linesDiff) {
+            for (var chunkNumber = lastDamagedChunkNumber + 1; chunkNumber < this._textChunks.length; ++chunkNumber)
+                this._textChunks[chunkNumber].startLine += linesDiff;
+        }
+
+        // Remove damaged chunks from DOM and from textChunks model.
+        var lastUndamagedChunk = firstDamagedChunkNumber > 0 ? this._textChunks[firstDamagedChunkNumber - 1] : null;
+        var firstUndamagedChunk = lastDamagedChunkNumber + 1 < this._textChunks.length ? this._textChunks[lastDamagedChunkNumber + 1] : null;
+
+        var removeDOMFromNode = lastUndamagedChunk ? lastUndamagedChunk.lastElement().nextSibling : this._container.firstChild;
+        var removeDOMToNode = firstUndamagedChunk ? firstUndamagedChunk.firstElement() : null;
+
+        // Fast case - patch single expanded chunk that did not grow / shrink during edit.
+        if (!linesDiff && firstDamagedChunk === lastDamagedChunk && firstDamagedChunk._expandedLineRows) {
+            var lastUndamagedLineRow = lastDamagedChunk.expandedLineRow(oldRange.startLine - 1);
+            var firstUndamagedLineRow = firstDamagedChunk.expandedLineRow(oldRange.endLine + 1);
+            var localRemoveDOMFromNode = lastUndamagedLineRow ? lastUndamagedLineRow.nextSibling : removeDOMFromNode;
+            var localRemoveDOMToNode = firstUndamagedLineRow || removeDOMToNode;
+            removeSubsequentNodes(localRemoveDOMFromNode, localRemoveDOMToNode);
+            for (var i = newRange.startLine; i < newRange.endLine + 1; ++i) {
+                var row = firstDamagedChunk._createRow(i);
+                firstDamagedChunk._expandedLineRows[i - firstDamagedChunk.startLine] = row;
+                this._container.insertBefore(row, localRemoveDOMToNode);
+            }
+            firstDamagedChunk.updateCollapsedLineRow();
+            this._assertDOMMatchesTextModel();
+            return;
+        }
+
+        removeSubsequentNodes(removeDOMFromNode, removeDOMToNode);
+        this._textChunks.splice(firstDamagedChunkNumber, lastDamagedChunkNumber - firstDamagedChunkNumber + 1);
+
+        // Compute damaged chunks span
+        var startLine = firstDamagedChunk.startLine;
+        var endLine = lastDamagedChunk.endLine + linesDiff;
+        var lineSpan = endLine - startLine;
+
+        // Re-create chunks for damaged area.
+        var insertionIndex = firstDamagedChunkNumber;
+        var chunkSize = Math.ceil(lineSpan / Math.ceil(lineSpan / this._defaultChunkSize));
+
+        for (var i = startLine; i < endLine; i += chunkSize) {
+            var chunk = this.createNewChunk(i, Math.min(endLine, i + chunkSize));
+            this._textChunks.splice(insertionIndex++, 0, chunk);
+            this._container.insertBefore(chunk.element, removeDOMToNode);
+        }
+
+        this._assertDOMMatchesTextModel();
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     */
+    _updateHighlightsForRange: function(range)
+    {
+        var visibleFrom = this.scrollTop();
+        var visibleTo = visibleFrom + this.clientHeight();
+
+        var result = this.findVisibleChunks(visibleFrom, visibleTo);
+        var chunk = this._textChunks[result.end - 1];
+        var lastVisibleLine = chunk.startLine + chunk.linesCount;
+
+        lastVisibleLine = Math.max(lastVisibleLine, range.endLine + 1);
+        lastVisibleLine = Math.min(lastVisibleLine, this._textModel.linesCount);
+
+        var updated = this._highlighter.updateHighlight(range.startLine, lastVisibleLine);
+        if (!updated) {
+            // Highlights for the chunks below are invalid, so just collapse them.
+            for (var i = this.chunkNumberForLine(range.startLine); i < this._textChunks.length; ++i)
+                this._textChunks[i].collapse();
+        }
+
+        this.repaintAll();
+    },
+
+    /**
+     * @param {Node} from
+     * @param {Node} to
+     * @return {Array.<string>}
+     */
+    _collectLinesFromDOM: function(from, to)
+    {
+        var textContents = [];
+        var hasContent = false;
+        for (var node = from ? from.nextSibling : this._container; node && node !== to; node = node.traverseNextNode(this._container)) {
+            // Skip all children of the decoration container and overlay highlight spans.
+            while (node && node !== to && (node._isDecorationsElement || node._isOverlayHighlightElement))
+                node = node.nextSibling;
+            if (!node || node === to)
+                break;
+
+            hasContent = true;
+            if (node.nodeName.toLowerCase() === "br")
+                textContents.push("\n");
+            else if (node.nodeType === Node.TEXT_NODE)
+                textContents.push(node.textContent);
+        }
+        if (!hasContent)
+            return [];
+
+        var textContent = textContents.join("");
+        // The last \n (if any) does not "count" in a DIV.
+        textContent = textContent.replace(/\n$/, "");
+
+        return textContent.split("\n");
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _handleSelectionChange: function(event)
+    {
+        var textRange = this.selection();
+        if (textRange)
+            this._lastSelection = textRange;
+
+        this._tokenHighlighter.handleSelectionChange(textRange);
+        this._braceHighlighter.handleSelectionChange(textRange);
+        this._delegate.selectionChanged(textRange);
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _handleTextInput: function(event)
+    {
+        this._textInputData = event.data;
+    },
+
+    /**
+     * @param {number} shortcutKey
+     * @param {Event} event
+     */
+    handleKeyDown: function(shortcutKey, event)
+    {
+        var handler = this._shortcuts[shortcutKey];
+        if (handler && handler()) {
+            event.consume(true);
+            return;
+        }
+
+        this._keyDownCode = event.keyCode;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _handleCut: function(event)
+    {
+        this._keyDownCode = WebInspector.KeyboardShortcut.Keys.Delete.code;
+    },
+
+    /**
+     * @param {number} scrollTop
+     * @param {number} clientHeight
+     * @param {number} chunkSize
+     */
+    overrideViewportForTest: function(scrollTop, clientHeight, chunkSize)
+    {
+        this._scrollTopOverrideForTest = scrollTop;
+        this._clientHeightOverrideForTest = clientHeight;
+        this._defaultChunkSize = chunkSize;
+    },
+
+    __proto__: WebInspector.TextEditorChunkedPanel.prototype
+}
+
+/**
+ * @interface
+ */
+WebInspector.TextEditorMainPanel.HighlightDescriptor = function() { }
+
+WebInspector.TextEditorMainPanel.HighlightDescriptor.prototype = {
+    /**
+     * @param {number} lineNumber
+     * @param {string} line
+     * @return {boolean}
+     */
+    affectsLine: function(lineNumber, line) { return false; },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string} line
+     * @return {Array.<{startColumn: number, endColumn: number}>}
+     */
+    rangesForLine: function(lineNumber, line) { return []; },
+
+    /**
+     * @return {string}
+     */
+    cssClass: function() { return ""; },
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TextEditorMainPanel.HighlightDescriptor}
+ */
+WebInspector.TextEditorMainPanel.RegexHighlightDescriptor = function(regex, cssClass)
+{
+    this._cssClass = cssClass;
+    this._regex = regex;
+}
+
+WebInspector.TextEditorMainPanel.RegexHighlightDescriptor.prototype = {
+    /**
+     * @param {number} lineNumber
+     * @param {string} line
+     * @return {boolean}
+     */
+    affectsLine: function(lineNumber, line)
+    {
+        this._regex.lastIndex = 0;
+        return this._regex.test(line);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string} line
+     * @return {Array.<{startColumn: number, endColumn: number}>}
+     */
+    rangesForLine: function(lineNumber, line)
+    {
+        var ranges = [];
+        var regexResult;
+        this._regex.lastIndex = 0;
+        while (regexResult = this._regex.exec(line)) {
+            ranges.push({
+                startColumn: regexResult.index,
+                endColumn: regexResult.index + regexResult[0].length - 1
+            });
+        }
+        return ranges;
+    },
+
+    /**
+     * @return {string}
+     */
+    cssClass: function()
+    {
+        return this._cssClass;
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TextEditorMainPanel.HighlightDescriptor}
+ * @param {WebInspector.TextRange} range
+ * @param {string} cssClass
+ */
+WebInspector.TextEditorMainPanel.RangeHighlightDescriptor = function(range, cssClass)
+{
+    this._cssClass = cssClass;
+    this._range = range;
+}
+
+WebInspector.TextEditorMainPanel.RangeHighlightDescriptor.prototype = {
+    /**
+     * @param {number} lineNumber
+     * @param {string} line
+     * @return {boolean}
+     */
+    affectsLine: function(lineNumber, line)
+    {
+        return this._range.startLine <= lineNumber && lineNumber <= this._range.endLine && line.length > 0;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string} line
+     * @return {Array.<{startColumn: number, endColumn: number}>}
+     */
+    rangesForLine: function(lineNumber, line)
+    {
+        if (!this.affectsLine(lineNumber, line))
+            return [];
+
+        var startColumn = lineNumber === this._range.startLine ? this._range.startColumn : 0;
+        var endColumn = lineNumber === this._range.endLine ? Math.min(this._range.endColumn, line.length) : line.length;
+        return [{
+            startColumn: startColumn,
+            endColumn: endColumn
+        }];
+    },
+
+    /**
+     * @return {string}
+     */
+    cssClass: function()
+    {
+        return this._cssClass;
+    }
+}
+
+/**
+ * @constructor
+ * @param {Element} element
+ */
+WebInspector.TextEditorMainPanel.ElementMetrics = function(element)
+{
+    this.width = element.offsetWidth;
+    this.height = element.offsetHeight;
+    this.left = element.offsetLeft;
+}
+
+/**
+ * @constructor
+ * @param {Array.<WebInspector.TextEditorMainPanel.ElementMetrics>} metrics
+ * @param {string} cssClass
+ */
+WebInspector.TextEditorMainPanel.LineOverlayHighlight = function(metrics, cssClass)
+{
+    this.metrics = metrics;
+    this.cssClass = cssClass;
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextEditorChunkedPanel} chunkedPanel
+ * @param {number} startLine
+ * @param {number} endLine
+ */
+WebInspector.TextEditorMainChunk = function(chunkedPanel, startLine, endLine)
+{
+    this._chunkedPanel = chunkedPanel;
+    this._textModel = chunkedPanel._textModel;
+
+    this.element = document.createElement("div");
+    this.element.lineNumber = startLine;
+    this.element.className = "webkit-line-content";
+    this.element._chunk = this;
+
+    this._startLine = startLine;
+    endLine = Math.min(this._textModel.linesCount, endLine);
+    this.linesCount = endLine - startLine;
+
+    this._expanded = false;
+
+    this.updateCollapsedLineRow();
+}
+
+WebInspector.TextEditorMainChunk.prototype = {
+    /**
+     * @param {Element|string} decoration
+     */
+    addDecoration: function(decoration)
+    {
+        this._chunkedPanel.beginDomUpdates();
+        if (typeof decoration === "string")
+            this.element.addStyleClass(decoration);
+        else {
+            if (!this.element.decorationsElement) {
+                this.element.decorationsElement = document.createElement("div");
+                this.element.decorationsElement.className = "webkit-line-decorations";
+                this.element.decorationsElement._isDecorationsElement = true;
+                this.element.appendChild(this.element.decorationsElement);
+            }
+            this.element.decorationsElement.appendChild(decoration);
+        }
+        this._chunkedPanel.endDomUpdates();
+    },
+
+    /**
+     * @param {string|Element} decoration
+     */
+    removeDecoration: function(decoration)
+    {
+        this._chunkedPanel.beginDomUpdates();
+        if (typeof decoration === "string")
+            this.element.removeStyleClass(decoration);
+        else if (this.element.decorationsElement)
+            this.element.decorationsElement.removeChild(decoration);
+        this._chunkedPanel.endDomUpdates();
+    },
+
+    removeAllDecorations: function()
+    {
+        this._chunkedPanel.beginDomUpdates();
+        this.element.className = "webkit-line-content";
+        if (this.element.decorationsElement) {
+            if (this.element.decorationsElement.parentElement)
+                this.element.removeChild(this.element.decorationsElement);
+            delete this.element.decorationsElement;
+        }
+        this._chunkedPanel.endDomUpdates();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isDecorated: function()
+    {
+        return this.element.className !== "webkit-line-content" || !!(this.element.decorationsElement && this.element.decorationsElement.firstChild);
+    },
+
+    /**
+     * @return {number}
+     */
+    get startLine()
+    {
+        return this._startLine;
+    },
+
+    /**
+     * @return {number}
+     */
+    get endLine()
+    {
+        return this._startLine + this.linesCount;
+    },
+
+    set startLine(startLine)
+    {
+        this._startLine = startLine;
+        this.element.lineNumber = startLine;
+        if (this._expandedLineRows) {
+            for (var i = 0; i < this._expandedLineRows.length; ++i)
+                this._expandedLineRows[i].lineNumber = startLine + i;
+        }
+    },
+
+    /**
+     * @return {boolean}
+     */
+    expanded: function()
+    {
+        return this._expanded;
+    },
+
+    expand: function()
+    {
+        if (this._expanded)
+            return;
+
+        this._expanded = true;
+
+        if (this.linesCount === 1) {
+            this._chunkedPanel._paintLines(this.startLine, this.startLine + 1);
+            return;
+        }
+
+        this._chunkedPanel.beginDomUpdates();
+
+        this._expandedLineRows = [];
+        var parentElement = this.element.parentElement;
+        for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) {
+            var lineRow = this._createRow(i);
+            parentElement.insertBefore(lineRow, this.element);
+            this._expandedLineRows.push(lineRow);
+        }
+        parentElement.removeChild(this.element);
+        this._chunkedPanel._paintLines(this.startLine, this.startLine + this.linesCount);
+
+        this._chunkedPanel.endDomUpdates();
+    },
+
+    collapse: function()
+    {
+        if (!this._expanded)
+            return;
+
+        this._expanded = false;
+        if (this.linesCount === 1)
+            return;
+
+        this._chunkedPanel.beginDomUpdates();
+
+        var elementInserted = false;
+        for (var i = 0; i < this._expandedLineRows.length; ++i) {
+            var lineRow = this._expandedLineRows[i];
+            var parentElement = lineRow.parentElement;
+            if (parentElement) {
+                if (!elementInserted) {
+                    elementInserted = true;
+                    parentElement.insertBefore(this.element, lineRow);
+                }
+                parentElement.removeChild(lineRow);
+            }
+            this._chunkedPanel._releaseLinesHighlight(lineRow);
+        }
+        delete this._expandedLineRows;
+
+        this._chunkedPanel.endDomUpdates();
+    },
+
+    /**
+     * @return {number}
+     */
+    get height()
+    {
+        if (!this._expandedLineRows)
+            return this._chunkedPanel.totalHeight(this.element);
+        return this._chunkedPanel.totalHeight(this._expandedLineRows[0], this._expandedLineRows[this._expandedLineRows.length - 1]);
+    },
+
+    /**
+     * @return {number}
+     */
+    get offsetTop()
+    {
+        return (this._expandedLineRows && this._expandedLineRows.length) ? this._expandedLineRows[0].offsetTop : this.element.offsetTop;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {Element}
+     */
+    _createRow: function(lineNumber)
+    {
+        var lineRow = this._chunkedPanel._cachedRows.pop() || document.createElement("div");
+        lineRow.lineNumber = lineNumber;
+        lineRow.className = "webkit-line-content";
+        lineRow.textContent = this._textModel.line(lineNumber);
+        if (!lineRow.textContent)
+            lineRow.appendChild(document.createElement("br"));
+        return lineRow;
+    },
+
+    /**
+     * Called on potentially damaged / inconsistent chunk
+     * @param {number} lineNumber
+     * @return {?Node}
+     */
+    lineRowContainingLine: function(lineNumber)
+    {
+        if (!this._expanded)
+            return this.element;
+        return this.expandedLineRow(lineNumber);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {Element}
+     */
+    expandedLineRow: function(lineNumber)
+    {
+        if (!this._expanded || lineNumber < this.startLine || lineNumber >= this.startLine + this.linesCount)
+            return null;
+        if (!this._expandedLineRows)
+            return this.element;
+        return this._expandedLineRows[lineNumber - this.startLine];
+    },
+
+    updateCollapsedLineRow: function()
+    {
+        if (this.linesCount === 1 && this._expanded)
+            return;
+
+        var lines = [];
+        for (var i = this.startLine; i < this.startLine + this.linesCount; ++i)
+            lines.push(this._textModel.line(i));
+
+        if (WebInspector.FALSE)
+            console.log("Rebuilding chunk with " + lines.length + " lines");
+
+        this.element.removeChildren();
+        this.element.textContent = lines.join("\n");
+        // The last empty line will get swallowed otherwise.
+        if (!lines[lines.length - 1])
+            this.element.appendChild(document.createElement("br"));
+    },
+
+    firstElement: function()
+    {
+        return this._expandedLineRows ? this._expandedLineRows[0] : this.element;
+    },
+
+    /**
+     * @return {Element}
+     */
+    lastElement: function()
+    {
+        return this._expandedLineRows ? this._expandedLineRows[this._expandedLineRows.length - 1] : this.element;
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextEditorMainPanel} mainPanel
+ * @param {WebInspector.TextEditorModel} textModel
+ */
+WebInspector.TextEditorMainPanel.TokenHighlighter = function(mainPanel, textModel)
+{
+    this._mainPanel = mainPanel;
+    this._textModel = textModel;
+}
+
+WebInspector.TextEditorMainPanel.TokenHighlighter.prototype = {
+    /**
+     * @param {WebInspector.TextRange} range
+     */
+    handleSelectionChange: function(range)
+    {
+        if (!range) {
+            this._removeHighlight();
+            return;
+        }
+
+        if (range.startLine !== range.endLine) {
+            this._removeHighlight();
+            return;
+        }
+
+        range = range.normalize();
+        var selectedText = this._textModel.copyRange(range);
+        if (selectedText === this._selectedWord)
+            return;
+
+        if (selectedText === "") {
+            this._removeHighlight();
+            return;
+        }
+
+        if (this._isWord(range, selectedText))
+            this._highlight(selectedText);
+        else
+            this._removeHighlight();
+    },
+
+    /**
+     * @param {string} word
+     */
+    _regexString: function(word)
+    {
+        return "\\b" + word + "\\b";
+    },
+
+    /**
+     * @param {string} selectedWord
+     */
+    _highlight: function(selectedWord)
+    {
+        this._removeHighlight();
+        this._selectedWord = selectedWord;
+        this._highlightDescriptor = this._mainPanel.highlightRegex(this._regexString(selectedWord), "text-editor-token-highlight")
+    },
+
+    _removeHighlight: function()
+    {
+        if (this._selectedWord) {
+            this._mainPanel.removeHighlight(this._highlightDescriptor);
+            delete this._selectedWord;
+            delete this._highlightDescriptor;
+        }
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} selectedText
+     * @return {boolean}
+     */
+    _isWord: function(range, selectedText)
+    {
+        var line = this._textModel.line(range.startLine);
+        var leftBound = range.startColumn === 0 || !WebInspector.TextUtils.isWordChar(line.charAt(range.startColumn - 1));
+        var rightBound = range.endColumn === line.length || !WebInspector.TextUtils.isWordChar(line.charAt(range.endColumn));
+        return leftBound && rightBound && WebInspector.TextUtils.isWord(selectedText);
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextEditorModel} textModel
+ * @param {WebInspector.TextEditor} textEditor
+ */
+WebInspector.DefaultTextEditor.WordMovementController = function(textEditor, textModel)
+{
+    this._textModel = textModel;
+    this._textEditor = textEditor;
+}
+
+WebInspector.DefaultTextEditor.WordMovementController.prototype = {
+
+    /**
+     * @param {Object.<number, function()>} shortcuts
+     */
+    _registerShortcuts: function(shortcuts)
+    {
+        var keys = WebInspector.KeyboardShortcut.Keys;
+        var modifiers = WebInspector.KeyboardShortcut.Modifiers;
+
+        const wordJumpModifier = WebInspector.isMac() ? modifiers.Alt : modifiers.Ctrl;
+        shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Backspace.code, wordJumpModifier)] = this._handleCtrlBackspace.bind(this);
+        shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Left.code, wordJumpModifier)] = this._handleCtrlArrow.bind(this, "left");
+        shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Right.code, wordJumpModifier)] = this._handleCtrlArrow.bind(this, "right");
+        shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Left.code, modifiers.Shift | wordJumpModifier)] = this._handleCtrlShiftArrow.bind(this, "left");
+        shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Right.code, modifiers.Shift | wordJumpModifier)] = this._handleCtrlShiftArrow.bind(this, "right");
+    },
+
+    /**
+     * @param {WebInspector.TextRange} selection
+     * @param {string} direction
+     * @return {WebInspector.TextRange}
+     */
+    _rangeForCtrlArrowMove: function(selection, direction)
+    {
+        const isStopChar = WebInspector.TextUtils.isStopChar;
+        const isSpaceChar = WebInspector.TextUtils.isSpaceChar;
+
+        var lineNumber = selection.endLine;
+        var column = selection.endColumn;
+        if (direction === "left")
+            --column;
+
+        if (column === -1 && direction === "left") {
+            if (lineNumber > 0)
+                return new WebInspector.TextRange(selection.startLine, selection.startColumn, lineNumber - 1, this._textModel.line(lineNumber - 1).length);
+            else
+                return selection.clone();
+        }
+
+        var line = this._textModel.line(lineNumber);
+        if (column === line.length && direction === "right") {
+            if (lineNumber + 1 < this._textModel.linesCount)
+                return new WebInspector.TextRange(selection.startLine, selection.startColumn, selection.endLine + 1, 0);
+            else
+                return selection.clone();
+        }
+
+        var delta = direction === "left" ? -1 : +1;
+        var directionDependentEndColumnOffset = (delta + 1) / 2;
+
+        if (isSpaceChar(line.charAt(column))) {
+            while(column + delta >= 0 && column + delta < line.length && isSpaceChar(line.charAt(column + delta)))
+                column += delta;
+            if (column + delta < 0 || column + delta === line.length)
+                return new WebInspector.TextRange(selection.startLine, selection.startColumn, lineNumber, column + directionDependentEndColumnOffset);
+            else
+                column += delta;
+        }
+
+        var group = isStopChar(line.charAt(column));
+
+        while(column + delta >= 0 && column + delta < line.length && isStopChar(line.charAt(column + delta)) === group && !isSpaceChar(line.charAt(column + delta)))
+            column += delta;
+
+        return new WebInspector.TextRange(selection.startLine, selection.startColumn, lineNumber, column + directionDependentEndColumnOffset);
+    },
+
+    /**
+     * @param {string} direction
+     * @return {boolean}
+     */
+    _handleCtrlArrow: function(direction)
+    {
+        var newSelection = this._rangeForCtrlArrowMove(this._textEditor.selection(), direction);
+        this._textEditor.setSelection(newSelection.collapseToEnd());
+        return true;
+    },
+
+    /**
+     * @param {string} direction
+     * @return {boolean}
+     */
+    _handleCtrlShiftArrow: function(direction)
+    {
+        this._textEditor.setSelection(this._rangeForCtrlArrowMove(this._textEditor.selection(), direction));
+        return true;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _handleCtrlBackspace: function()
+    {
+        var selection = this._textEditor.selection();
+        if (!selection.isEmpty())
+            return false;
+
+        var newSelection = this._rangeForCtrlArrowMove(selection, "left");
+        this._textModel.editRange(newSelection.normalize(), "", selection);
+
+        this._textEditor.setSelection(newSelection.collapseToEnd());
+        return true;
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextEditorMainPanel} textEditor
+ * @param {WebInspector.TextEditorModel} textModel
+ * @param {WebInspector.TextEditorModel.BraceMatcher} braceMatcher
+ */
+WebInspector.TextEditorMainPanel.BraceHighlightController = function(textEditor, textModel, braceMatcher)
+{
+    this._textEditor = textEditor;
+    this._textModel = textModel;
+    this._braceMatcher = braceMatcher;
+    this._highlightDescriptors = [];
+}
+
+WebInspector.TextEditorMainPanel.BraceHighlightController.prototype = {
+    /**
+     * @param {string} line
+     * @param {number} column
+     * @return {number}
+     */
+    activeBraceColumnForCursorPosition: function(line, column)
+    {
+        var char = line.charAt(column);
+        if (WebInspector.TextUtils.isOpeningBraceChar(char))
+            return column;
+
+        var previousChar = line.charAt(column - 1);
+        if (WebInspector.TextUtils.isBraceChar(previousChar))
+            return column - 1;
+
+        if (WebInspector.TextUtils.isBraceChar(char))
+            return column;
+        else
+            return -1;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} selectionRange
+     */
+    handleSelectionChange: function(selectionRange)
+    {
+        if (!selectionRange || !selectionRange.isEmpty()) {
+            this._removeHighlight();
+            return;
+        }
+
+        if (this._highlightedRange && this._highlightedRange.compareTo(selectionRange) === 0)
+            return;
+
+        this._removeHighlight();
+        var lineNumber = selectionRange.startLine;
+        var column = selectionRange.startColumn;
+        var line = this._textModel.line(lineNumber);
+        column = this.activeBraceColumnForCursorPosition(line, column);
+        if (column < 0)
+            return;
+
+        var enclosingBraces = this._braceMatcher.enclosingBraces(lineNumber, column);
+        if (!enclosingBraces)
+            return;
+
+        this._highlightedRange = selectionRange;
+        this._highlightDescriptors.push(this._textEditor.highlightRange(WebInspector.TextRange.createFromLocation(enclosingBraces.leftBrace.lineNumber, enclosingBraces.leftBrace.column), "text-editor-brace-match"));
+        this._highlightDescriptors.push(this._textEditor.highlightRange(WebInspector.TextRange.createFromLocation(enclosingBraces.rightBrace.lineNumber, enclosingBraces.rightBrace.column), "text-editor-brace-match"));
+    },
+
+    _removeHighlight: function()
+    {
+        if (!this._highlightDescriptors.length)
+            return;
+
+        for(var i = 0; i < this._highlightDescriptors.length; ++i)
+            this._textEditor.removeHighlight(this._highlightDescriptors[i]);
+
+        this._highlightDescriptors = [];
+        delete this._highlightedRange;
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextEditorMainPanel} mainPanel
+ * @param {WebInspector.TextEditorModel} textModel
+ * @param {WebInspector.TextEditorModel.BraceMatcher} braceMatcher
+ */
+WebInspector.TextEditorMainPanel.SmartBraceController = function(mainPanel, textModel, braceMatcher)
+{
+    this._mainPanel = mainPanel;
+    this._textModel = textModel;
+    this._braceMatcher = braceMatcher
+}
+
+WebInspector.TextEditorMainPanel.SmartBraceController.prototype = {
+    /**
+     * @param {Object.<number, function()>} shortcuts
+     */
+    registerShortcuts: function(shortcuts)
+    {
+        var keys = WebInspector.KeyboardShortcut.Keys;
+        var modifiers = WebInspector.KeyboardShortcut.Modifiers;
+
+        shortcuts[WebInspector.KeyboardShortcut.makeKey(keys.Backspace.code, modifiers.None)] = this._handleBackspace.bind(this);
+    },
+
+    /**
+     * @param {Object.<string, function()>} charOverrides
+     */
+    registerCharOverrides: function(charOverrides)
+    {
+        charOverrides["("] = this._handleBracePairInsertion.bind(this, "()");
+        charOverrides[")"] = this._handleClosingBraceOverride.bind(this, ")");
+        charOverrides["{"] = this._handleBracePairInsertion.bind(this, "{}");
+        charOverrides["}"] = this._handleClosingBraceOverride.bind(this, "}");
+    },
+
+    _handleBackspace: function()
+    {
+        var selection = this._mainPanel.lastSelection();
+        if (!selection || !selection.isEmpty())
+            return false;
+
+        var column = selection.startColumn;
+        if (column == 0)
+            return false;
+
+        var lineNumber = selection.startLine;
+        var line = this._textModel.line(lineNumber);
+        if (column === line.length)
+            return false;
+
+        var pair = line.substr(column - 1, 2);
+        if (pair === "()" || pair === "{}") {
+            this._textModel.editRange(new WebInspector.TextRange(lineNumber, column - 1, lineNumber, column + 1), "");
+            this._mainPanel.setSelection(WebInspector.TextRange.createFromLocation(lineNumber, column - 1));
+            return true;
+        } else
+            return false;
+    },
+
+    /**
+     * @param {string} bracePair
+     * @return {boolean}
+     */
+    _handleBracePairInsertion: function(bracePair)
+    {
+        var selection = this._mainPanel.lastSelection().normalize();
+        if (selection.isEmpty()) {
+            var lineNumber = selection.startLine;
+            var column = selection.startColumn;
+            var line = this._textModel.line(lineNumber);
+            if (column < line.length) {
+                var char = line.charAt(column);
+                if (WebInspector.TextUtils.isWordChar(char) || (!WebInspector.TextUtils.isBraceChar(char) && WebInspector.TextUtils.isStopChar(char)))
+                    return false;
+            }
+        }
+        this._textModel.editRange(selection, bracePair);
+        this._mainPanel.setSelection(WebInspector.TextRange.createFromLocation(selection.startLine, selection.startColumn + 1));
+        return true;
+    },
+
+    /**
+     * @param {string} brace
+     * @return {boolean}
+     */
+    _handleClosingBraceOverride: function(brace)
+    {
+        var selection = this._mainPanel.lastSelection().normalize();
+        if (!selection || !selection.isEmpty())
+            return false;
+
+        var lineNumber = selection.startLine;
+        var column = selection.startColumn;
+        var line = this._textModel.line(lineNumber);
+        if (line.charAt(column) !== brace)
+            return false;
+
+        var braces = this._braceMatcher.enclosingBraces(lineNumber, column);
+        if (braces && braces.rightBrace.lineNumber === lineNumber && braces.rightBrace.column === column) {
+            this._mainPanel.setSelection(WebInspector.TextRange.createFromLocation(lineNumber, column + 1));
+            return true;
+        } else
+            return false;
+    },
+}
+
+WebInspector.debugDefaultTextEditor = false;
diff --git a/Source/devtools/front_end/DevToolsExtensionAPI.js b/Source/devtools/front_end/DevToolsExtensionAPI.js
new file mode 100644
index 0000000..44f7c21
--- /dev/null
+++ b/Source/devtools/front_end/DevToolsExtensionAPI.js
@@ -0,0 +1,65 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+function platformExtensionAPI(coreAPI)
+{
+    function getTabId()
+    {
+        return tabId;
+    }
+    chrome = window.chrome || {};
+    // Override chrome.devtools as a workaround for a error-throwing getter being exposed
+    // in extension pages loaded into a non-extension process (only happens for remote client
+    // extensions)
+    var devtools_descriptor = Object.getOwnPropertyDescriptor(chrome, "devtools");
+    if (!devtools_descriptor || devtools_descriptor.get)
+        Object.defineProperty(chrome, "devtools", { value: {}, enumerable: true });
+    // Only expose tabId on chrome.devtools.inspectedWindow, not webInspector.inspectedWindow.
+    chrome.devtools.inspectedWindow = {};
+    chrome.devtools.inspectedWindow.__defineGetter__("tabId", getTabId);
+    chrome.devtools.inspectedWindow.__proto__ = coreAPI.inspectedWindow;
+    chrome.devtools.network = coreAPI.network;
+    chrome.devtools.panels = coreAPI.panels;
+
+    // default to expose experimental APIs for now.
+    if (extensionInfo.exposeExperimentalAPIs !== false) {
+        chrome.experimental = chrome.experimental || {};
+        chrome.experimental.devtools = chrome.experimental.devtools || {};
+
+        var properties = Object.getOwnPropertyNames(coreAPI);
+        for (var i = 0; i < properties.length; ++i) {
+            var descriptor = Object.getOwnPropertyDescriptor(coreAPI, properties[i]);
+            Object.defineProperty(chrome.experimental.devtools, properties[i], descriptor);
+        }
+        chrome.experimental.devtools.inspectedWindow = chrome.devtools.inspectedWindow;
+    }
+    if (extensionInfo.exposeWebInspectorNamespace)
+        window.webInspector = coreAPI;
+}
diff --git a/Source/devtools/front_end/Dialog.js b/Source/devtools/front_end/Dialog.js
new file mode 100644
index 0000000..1930056
--- /dev/null
+++ b/Source/devtools/front_end/Dialog.js
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {Element} relativeToElement
+ * @param {WebInspector.DialogDelegate} delegate
+ */
+WebInspector.Dialog = function(relativeToElement, delegate)
+{
+    this._delegate = delegate;
+    this._relativeToElement = relativeToElement;
+
+    this._glassPane = new WebInspector.GlassPane();
+    // Install glass pane capturing events.
+    this._glassPane.element.tabIndex = 0;
+    this._glassPane.element.addEventListener("focus", this._onGlassPaneFocus.bind(this), false);
+
+    this._element = this._glassPane.element.createChild("div");
+    this._element.tabIndex = 0;
+    this._element.addEventListener("focus", this._onFocus.bind(this), false);
+    this._element.addEventListener("keydown", this._onKeyDown.bind(this), false);
+    this._closeKeys = [
+        WebInspector.KeyboardShortcut.Keys.Enter.code,
+        WebInspector.KeyboardShortcut.Keys.Esc.code,
+    ];
+
+    delegate.show(this._element);
+
+    this._position();
+    this._windowResizeHandler = this._position.bind(this);
+    window.addEventListener("resize", this._windowResizeHandler, true);
+    this._delegate.focus();
+}
+
+/**
+ * @return {WebInspector.Dialog}
+ */
+WebInspector.Dialog.currentInstance = function()
+{
+    return WebInspector.Dialog._instance;
+}
+
+/**
+ * @param {Element} relativeToElement
+ * @param {WebInspector.DialogDelegate} delegate
+ */
+WebInspector.Dialog.show = function(relativeToElement, delegate)
+{
+    if (WebInspector.Dialog._instance)
+        return;
+    WebInspector.Dialog._instance = new WebInspector.Dialog(relativeToElement, delegate);
+}
+
+WebInspector.Dialog.hide = function()
+{
+    if (!WebInspector.Dialog._instance)
+        return;
+    WebInspector.Dialog._instance._hide();
+}
+
+WebInspector.Dialog.prototype = {
+    _hide: function()
+    {
+        if (this._isHiding)
+            return;
+        this._isHiding = true;
+
+        this._delegate.willHide();
+
+        delete WebInspector.Dialog._instance;
+        this._glassPane.dispose();
+        window.removeEventListener("resize", this._windowResizeHandler, true);
+    },
+
+    _onGlassPaneFocus: function(event)
+    {
+        this._hide();
+    },
+
+    _onFocus: function(event)
+    {
+        this._delegate.focus();
+    },
+
+    _position: function()
+    {
+        this._delegate.position(this._element, this._relativeToElement);
+    },
+
+    _onKeyDown: function(event)
+    {
+        if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Tab.code) {
+            event.preventDefault();
+            return;
+        }
+
+        if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Enter.code)
+            this._delegate.onEnter();
+
+        if (this._closeKeys.indexOf(event.keyCode) >= 0) {
+            this._hide();
+            event.consume(true);
+        }
+    }
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.DialogDelegate = function()
+{
+}
+
+WebInspector.DialogDelegate.prototype = {
+    /**
+     * @param {Element} element
+     */
+    show: function(element)
+    {
+        element.appendChild(this.element);
+        this.element.addStyleClass("dialog-contents");
+        element.addStyleClass("dialog");    
+    },
+
+    /**
+     * @param {Element} element
+     * @param {Element} relativeToElement
+     */
+    position: function(element, relativeToElement)
+    {
+        var offset = relativeToElement.offsetRelativeToWindow(window);
+
+        var positionX = offset.x + (relativeToElement.offsetWidth - element.offsetWidth) / 2;
+        positionX = Number.constrain(positionX, 0, window.innerWidth - element.offsetWidth);
+
+        var positionY = offset.y + (relativeToElement.offsetHeight - element.offsetHeight) / 2;
+        positionY = Number.constrain(positionY, 0, window.innerHeight - element.offsetHeight);
+
+        element.style.left = positionX + "px";
+        element.style.top = positionY + "px";
+    },
+
+    focus: function() { },
+
+    onEnter: function() { },
+
+    willHide: function() { },
+
+    __proto__: WebInspector.Object.prototype
+}
+
diff --git a/Source/devtools/front_end/DirectoryContentView.js b/Source/devtools/front_end/DirectoryContentView.js
new file mode 100644
index 0000000..abd969a
--- /dev/null
+++ b/Source/devtools/front_end/DirectoryContentView.js
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGrid}
+ */
+WebInspector.DirectoryContentView = function()
+{
+    const indexes = WebInspector.DirectoryContentView.columnIndexes;
+    var columns = [
+        {id: indexes.Name, title: WebInspector.UIString("Name"), sortable: true, sort: WebInspector.DataGrid.Order.Ascending, width: "20%"},
+        {id: indexes.URL, title: WebInspector.UIString("URL"), sortable: true, width: "20%"},
+        {id: indexes.Type, title: WebInspector.UIString("Type"), sortable: true, width: "15%"},
+        {id: indexes.Size, title: WebInspector.UIString("Size"), sortable: true, width: "10%"},
+        {id: indexes.ModificationTime, title: WebInspector.UIString("Modification Time"), sortable: true, width: "25%"}
+    ];
+
+    WebInspector.DataGrid.call(this, columns);
+    this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sort, this);
+}
+
+WebInspector.DirectoryContentView.columnIndexes = {
+    Name: "0",
+    URL: "1",
+    Type: "2",
+    Size: "3",
+    ModificationTime: "4"
+}
+
+WebInspector.DirectoryContentView.prototype = {
+    /**
+     * @param {Array.<WebInspector.FileSystemModel.Directory>} entries
+     */
+    showEntries: function(entries)
+    {
+        const indexes = WebInspector.DirectoryContentView.columnIndexes;
+        this.rootNode().removeChildren();
+        for (var i = 0; i < entries.length; ++i)
+            this.rootNode().appendChild(new WebInspector.DirectoryContentView.Node(entries[i]));
+    },
+
+    _sort: function()
+    {
+        var column = /** @type {string} */ (this.sortColumnIdentifier());
+        this.sortNodes(WebInspector.DirectoryContentView.Node.comparator(column, !this.isSortOrderAscending()), false);
+    },
+
+    __proto__: WebInspector.DataGrid.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ * @param {WebInspector.FileSystemModel.Entry} entry
+ */
+WebInspector.DirectoryContentView.Node = function(entry)
+{
+    const indexes = WebInspector.DirectoryContentView.columnIndexes;
+    var data = {};
+    data[indexes.Name] = entry.name;
+    data[indexes.URL] = entry.url;
+    data[indexes.Type] = entry.isDirectory ? WebInspector.UIString("Directory") : entry.mimeType;
+    data[indexes.Size] = "";
+    data[indexes.ModificationTime] = "";
+
+    WebInspector.DataGridNode.call(this, data);
+    this._entry = entry;
+    this._metadata = null;
+
+    this._entry.requestMetadata(this._metadataReceived.bind(this));
+}
+
+/**
+ * @param {string} column
+ * @param {boolean} reverse
+ */
+WebInspector.DirectoryContentView.Node.comparator = function(column, reverse)
+{
+    var reverseFactor = reverse ? -1 : 1;
+    const indexes = WebInspector.DirectoryContentView.columnIndexes;
+
+    switch (column) {
+    case indexes.Name:
+    case indexes.URL:
+        return function(x, y)
+        {
+            return isDirectoryCompare(x, y) || nameCompare(x, y);
+        };
+    case indexes.Type:
+        return function(x, y)
+        {
+            return isDirectoryCompare(x ,y) || typeCompare(x, y) || nameCompare(x, y);
+        };
+    case indexes.Size:
+        return function(x, y)
+        {
+            return isDirectoryCompare(x, y) || sizeCompare(x, y) || nameCompare(x, y);
+        };
+    case indexes.ModificationTime:
+        return function(x, y)
+        {
+            return isDirectoryCompare(x, y) || modificationTimeCompare(x, y) || nameCompare(x, y);
+        };
+    }
+
+    function isDirectoryCompare(x, y)
+    {
+        if (x._entry.isDirectory != y._entry.isDirectory)
+            return y._entry.isDirectory ? 1 : -1;
+        return 0;
+    }
+
+    function nameCompare(x, y)
+    {
+        return reverseFactor * x._entry.name.compareTo(y._entry.name);
+    }
+
+    function typeCompare(x, y)
+    {
+        return reverseFactor * (x._entry.mimeType || "").compareTo(y._entry.mimeType || "");
+    }
+
+    function sizeCompare(x, y)
+    {
+        return reverseFactor * ((x._metadata ? x._metadata.size : 0) - (y._metadata ? y._metadata.size : 0));
+    }
+
+    function modificationTimeCompare(x, y)
+    {
+        return reverseFactor * ((x._metadata ? x._metadata.modificationTime : 0) - (y._metadata ? y._metadata.modificationTime : 0));
+    }
+}
+
+WebInspector.DirectoryContentView.Node.prototype = {
+    /**
+     * @param {number} errorCode
+     * @param {FileSystemAgent.Metadata} metadata
+     */
+    _metadataReceived: function(errorCode, metadata)
+    {
+        const indexes = WebInspector.DirectoryContentView.columnIndexes;
+        if (errorCode !== 0)
+            return;
+
+        this._metadata = metadata;
+        var data = this.data;
+        if (this._entry.isDirectory)
+            data[indexes.Size] = WebInspector.UIString("-");
+        else
+            data[indexes.Size] = Number.bytesToString(metadata.size);
+        data[indexes.ModificationTime] = new Date(metadata.modificationTime).toGMTString();
+        this.data = data;
+    },
+
+    __proto__: WebInspector.DataGridNode.prototype
+}
diff --git a/Source/devtools/front_end/DockController.js b/Source/devtools/front_end/DockController.js
new file mode 100644
index 0000000..b497530
--- /dev/null
+++ b/Source/devtools/front_end/DockController.js
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.DockController = function()
+{
+    this._dockToggleButton = new WebInspector.StatusBarButton("", "dock-status-bar-item", 3);
+    this._dockToggleButtonOption = new WebInspector.StatusBarButton("", "dock-status-bar-item", 3);
+    this._dockToggleButton.addEventListener("click", this._toggleDockState, this);
+    this._dockToggleButtonOption.addEventListener("click", this._toggleDockState, this);
+    this._dockToggleButton.makeLongClickOptionsEnabled(this._createDockOptions.bind(this));
+
+    this.setDockSide(WebInspector.queryParamsObject["dockSide"] || "bottom");
+    WebInspector.settings.showToolbarIcons.addChangeListener(this._updateUI.bind(this));
+}
+
+WebInspector.DockController.State = {
+    DockedToBottom: "bottom",
+    DockedToRight: "right",
+    Undocked: "undocked"
+}
+
+WebInspector.DockController.Events = {
+    DockSideChanged: "DockSideChanged"
+}
+
+WebInspector.DockController.prototype = {
+    /**
+     * @return {Element}
+     */
+    get element()
+    {
+        return this._dockToggleButton.element;
+    },
+
+    /**
+     * @return {string}
+     */
+    dockSide: function()
+    {
+        return this._dockSide;
+    },
+
+    /**
+     * @param {string} dockSide
+     */
+    setDockSide: function(dockSide)
+    {
+        if (this._dockSide === dockSide)
+            return;
+
+        if (this._dockSide)
+            WebInspector.settings.lastDockState.set(this._dockSide);
+
+        this._dockSide = dockSide;
+        if (dockSide === WebInspector.DockController.State.Undocked) 
+            WebInspector.userMetrics.WindowDocked.record();
+        else
+            WebInspector.userMetrics.WindowUndocked.record();
+        this._updateUI();
+        this.dispatchEventToListeners(WebInspector.DockController.Events.DockSideChanged, this._dockSide);
+    },
+
+    /**
+     * @param {boolean} unavailable
+     */
+    setDockingUnavailable: function(unavailable)
+    {
+        this._isDockingUnavailable = unavailable;
+        this._updateUI();
+    },
+
+    _updateUI: function()
+    {
+        var body = document.body;
+        switch (this._dockSide) {
+        case WebInspector.DockController.State.DockedToBottom:
+            body.removeStyleClass("undocked");
+            body.removeStyleClass("dock-to-right");
+            body.addStyleClass("dock-to-bottom");
+            break;
+        case WebInspector.DockController.State.DockedToRight: 
+            body.removeStyleClass("undocked");
+            body.addStyleClass("dock-to-right");
+            body.removeStyleClass("dock-to-bottom");
+            break;
+        case WebInspector.DockController.State.Undocked: 
+            body.addStyleClass("undocked");
+            body.removeStyleClass("dock-to-right");
+            body.removeStyleClass("dock-to-bottom");
+            break;
+        }
+
+        if (WebInspector.settings.showToolbarIcons.get())
+            document.body.addStyleClass("show-toolbar-icons");
+        else
+            document.body.removeStyleClass("show-toolbar-icons");
+
+        if (this._isDockingUnavailable && this._dockSide === WebInspector.DockController.State.Undocked) {
+            this._dockToggleButton.state = "undock";
+            this._dockToggleButton.setEnabled(false);
+            return;
+        }
+
+        this._dockToggleButton.setEnabled(true);
+
+        // Choose different last state based on the current one if missing or if is the same.
+        var sides = [WebInspector.DockController.State.DockedToBottom, WebInspector.DockController.State.Undocked, WebInspector.DockController.State.DockedToRight];
+        sides.remove(this._dockSide);
+        var lastState = WebInspector.settings.lastDockState.get();
+
+        sides.remove(lastState);
+        if (sides.length === 2) { // last state was not from the list of potential values
+            lastState = sides[0];
+            sides.remove(lastState);
+        }
+        this._decorateButtonForTargetState(this._dockToggleButton, lastState);
+        this._decorateButtonForTargetState(this._dockToggleButtonOption, sides[0]);
+    },
+
+    /**
+     * @param {WebInspector.StatusBarButton} button
+     * @param {string} state
+     */
+    _decorateButtonForTargetState: function(button, state)
+    {
+        switch (state) {
+        case WebInspector.DockController.State.DockedToBottom:
+            button.title = WebInspector.UIString("Dock to main window.");
+            button.state = "bottom";
+            break;
+        case WebInspector.DockController.State.DockedToRight:
+            button.title = WebInspector.UIString("Dock to main window.");
+            button.state = "right";
+            break;
+        case WebInspector.DockController.State.Undocked: 
+            button.title = WebInspector.UIString("Undock into separate window.");
+            button.state = "undock";
+            break;
+        }
+    },
+
+    _createDockOptions: function()
+    {
+        return [this._dockToggleButtonOption];
+    },
+
+    /**
+     * @param {WebInspector.Event} e
+     */
+    _toggleDockState: function(e)
+    {
+        var action;
+        switch (e.target.state) {
+        case "bottom": action = "bottom"; break;
+        case "right": action = "right"; break;
+        case "undock": action = "undocked"; break;
+        }
+        InspectorFrontendHost.requestSetDockSide(action);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @type {?WebInspector.DockController}
+ */
+WebInspector.dockController = null;
diff --git a/Source/devtools/front_end/Drawer.js b/Source/devtools/front_end/Drawer.js
new file mode 100644
index 0000000..3a67de1
--- /dev/null
+++ b/Source/devtools/front_end/Drawer.js
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2007, 2008 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 Computer, 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.Drawer = function()
+{
+    this.element = document.getElementById("drawer");
+    this.element.style.height = 0;
+
+    this._savedHeight = 200; // Default.
+    this._mainElement = document.getElementById("main");
+    this._toolbarElement = document.getElementById("toolbar");
+
+    this._floatingStatusBarContainer = document.getElementById("floating-status-bar-container");
+    WebInspector.installDragHandle(this._floatingStatusBarContainer, this._startStatusBarDragging.bind(this), this._statusBarDragging.bind(this), this._endStatusBarDragging.bind(this), "row-resize");
+
+    this._drawerContentsElement = document.createElement("div");
+    this._drawerContentsElement.id = "drawer-contents";
+    this._drawerContentsElement.className = "drawer-contents";
+    this.element.appendChild(this._drawerContentsElement);
+    this._viewStatusBar = document.createElement("div");
+    this._viewStatusBar.addEventListener("webkitTransitionEnd", this.immediatelyFinishAnimation.bind(this), false);
+    this._viewStatusBar.style.opacity = 0;
+    this._bottomStatusBar = document.getElementById("bottom-status-bar-container");
+
+    var drawerIsOverlay = WebInspector.experimentsSettings.drawerOverlay.isEnabled();
+    this._elementToAdjust = drawerIsOverlay ?  this._floatingStatusBarContainer : this._mainElement;
+
+    document.body.enableStyleClass("drawer-overlay", drawerIsOverlay);
+}
+
+WebInspector.Drawer.AnimationType = {
+        Immediately: 0,
+        Normal: 1,
+        Slow: 2
+}
+
+WebInspector.Drawer.prototype = {
+    get visible()
+    {
+        return !!this._view;
+    },
+
+    _constrainHeight: function(height)
+    {
+        return Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - this._mainElement.totalOffsetTop() - Preferences.minConsoleHeight);
+    },
+
+    show: function(view, animationType)
+    {
+        this.immediatelyFinishAnimation();
+
+        var drawerWasVisible = this.visible;
+
+        if (this._view) {
+            this._view.detach();
+            this._drawerContentsElement.removeChildren();
+        }
+
+        this._view = view;
+
+        var statusBarItems = this._view.statusBarItems || [];
+        this._viewStatusBar.removeChildren();
+        for (var i = 0; i < statusBarItems.length; ++i)
+            this._viewStatusBar.appendChild(statusBarItems[i]);
+
+        document.body.addStyleClass("drawer-visible");
+        this._floatingStatusBarContainer.insertBefore(document.getElementById("panel-status-bar"), this._floatingStatusBarContainer.firstElementChild);
+        this._bottomStatusBar.appendChild(this._viewStatusBar);
+        this._view.detach();
+        this._view.markAsRoot();
+        this._view.show(this._drawerContentsElement);
+
+        if (drawerWasVisible)
+            return;
+        
+        var height = this._constrainHeight(this._savedHeight || this.element.offsetHeight);
+
+        this._floatingStatusBarContainer.style.paddingLeft = this._bottomStatusBar.offsetLeft + "px";
+
+        function animationFinished()
+        {
+            WebInspector.inspectorView.currentPanel().doResize();
+            if (this._view && this._view.afterShow)
+                this._view.afterShow();
+        }
+
+        this._animationFinished = animationFinished.bind(this);
+
+        // Assert that transition will be done and we receive transitionEnd event
+        console.assert(this._viewStatusBar.style.opacity === "0");
+
+        function adjustStyles()
+        {
+            this._animationStyles(animationType).forEach(document.body.addStyleClass, document.body);
+
+            this.element.style.height = height + "px";
+            this._elementToAdjust.style.bottom = height + "px";
+            this._floatingStatusBarContainer.style.paddingLeft = 0;
+            this._viewStatusBar.style.opacity = 1;
+        }
+
+        if (animationType === WebInspector.Drawer.AnimationType.Immediately) {
+            adjustStyles.call(this);
+            this.immediatelyFinishAnimation();
+        } else
+            setTimeout(adjustStyles.bind(this), 0);
+    },
+
+    hide: function(animationType)
+    {
+        this.immediatelyFinishAnimation();
+        if (!this.visible)
+            return;
+
+        this._savedHeight = this.element.offsetHeight;
+
+        WebInspector.restoreFocusFromElement(this.element);
+
+        // Temporarily set properties and classes to mimic the post-animation values so panels
+        // like Elements in their updateStatusBarItems call will size things to fit the final location.
+        document.body.removeStyleClass("drawer-visible");
+        WebInspector.inspectorView.currentPanel().statusBarResized();
+        document.body.addStyleClass("drawer-visible");
+
+        function animationFinished()
+        {
+            WebInspector.inspectorView.currentPanel().doResize();
+            this._view.detach();
+            delete this._view;
+            this._bottomStatusBar.removeChildren();
+            this._bottomStatusBar.appendChild(document.getElementById("panel-status-bar"));
+            this._drawerContentsElement.removeChildren();
+            document.body.removeStyleClass("drawer-visible");
+        }
+
+        this._animationFinished = animationFinished.bind(this);
+
+        // Assert that transition will be done and we receive transitionEnd event
+        console.assert(this._viewStatusBar.style.opacity === "1");
+
+        function adjustStyles()
+        {
+            this._animationStyles(animationType).forEach(document.body.addStyleClass, document.body);
+
+            this.element.style.height = 0;
+            this._elementToAdjust.style.bottom = 0;
+            this._floatingStatusBarContainer.style.paddingLeft = this._bottomStatusBar.offsetLeft + "px";
+            this._viewStatusBar.style.opacity = 0;
+        }
+
+        if (animationType === WebInspector.Drawer.AnimationType.Immediately) {
+            adjustStyles.call(this);
+            this.immediatelyFinishAnimation();
+        } else
+            setTimeout(adjustStyles.bind(this), 0);
+    },
+
+    resize: function()
+    {
+        if (!this.visible)
+            return;
+
+        this._view.storeScrollPositions();
+        var height = this._constrainHeight(parseInt(this.element.style.height, 10));
+        this._elementToAdjust.style.bottom = height + "px";
+        this.element.style.height = height + "px";
+        this._view.doResize();
+    },
+
+    immediatelyFinishAnimation: function()
+    {
+        document.body.removeStyleClass("animate");
+        document.body.removeStyleClass("animate-slow");
+        if (this._animationFinished) {
+            this._animationFinished();
+            delete this._animationFinished;
+        }
+    },
+
+    _animationStyles: function(animationType)
+    {
+        switch (animationType) {
+        case WebInspector.Drawer.AnimationType.Slow:
+            return ["animate", "animate-slow"];
+        case WebInspector.Drawer.AnimationType.Normal:
+            return ["animate"];
+        default:
+            return [];
+        }
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _startStatusBarDragging: function(event)
+    {
+        if (!this.visible || event.target !== this._floatingStatusBarContainer)
+            return false;
+
+        this._view.storeScrollPositions();
+        this._statusBarDragOffset = event.pageY - this.element.totalOffsetTop();
+        return true;
+    },
+
+    _statusBarDragging: function(event)
+    {
+        var height = window.innerHeight - event.pageY + this._statusBarDragOffset;
+        height = Number.constrain(height, Preferences.minConsoleHeight, window.innerHeight - this._mainElement.totalOffsetTop() - Preferences.minConsoleHeight);
+
+        this._elementToAdjust.style.bottom = height + "px";
+        this.element.style.height = height + "px";
+        if (WebInspector.inspectorView.currentPanel())
+            WebInspector.inspectorView.currentPanel().doResize();
+        this._view.doResize();
+
+        event.consume(true);
+    },
+
+    _endStatusBarDragging: function(event)
+    {
+        this._savedHeight = this.element.offsetHeight;
+        delete this._statusBarDragOffset;
+
+        event.consume();
+    }
+}
+
+/**
+ * @type {WebInspector.Drawer}
+ */
+WebInspector.drawer = null;
diff --git a/Source/devtools/front_end/ElementsPanel.js b/Source/devtools/front_end/ElementsPanel.js
new file mode 100755
index 0000000..b29c92d
--- /dev/null
+++ b/Source/devtools/front_end/ElementsPanel.js
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
+ * 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 Computer, 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.
+ */
+
+importScript("CSSNamedFlowCollectionsView.js");
+importScript("CSSNamedFlowView.js");
+importScript("EventListenersSidebarPane.js");
+importScript("MetricsSidebarPane.js");
+importScript("PropertiesSidebarPane.js");
+importScript("StylesSidebarPane.js");
+
+/**
+ * @constructor
+ * @extends {WebInspector.Panel}
+ */
+WebInspector.ElementsPanel = function()
+{
+    WebInspector.Panel.call(this, "elements");
+    this.registerRequiredCSS("breadcrumbList.css");
+    this.registerRequiredCSS("elementsPanel.css");
+    this.registerRequiredCSS("textPrompt.css");
+    this.setHideOnDetach();
+
+    const initialSidebarWidth = 325;
+    const minimumContentWidthPercent = 34;
+    const initialSidebarHeight = 325;
+    const minimumContentHeightPercent = 34;
+    this.createSidebarView(this.element, WebInspector.SidebarView.SidebarPosition.End, initialSidebarWidth, initialSidebarHeight);
+    this.splitView.setMinimumSidebarWidth(Preferences.minElementsSidebarWidth);
+    this.splitView.setMinimumMainWidthPercent(minimumContentWidthPercent);
+    this.splitView.setMinimumSidebarHeight(Preferences.minElementsSidebarHeight);
+    this.splitView.setMinimumMainHeightPercent(minimumContentHeightPercent);
+
+    this.contentElement = this.splitView.mainElement;
+    this.contentElement.id = "elements-content";
+    this.contentElement.addStyleClass("outline-disclosure");
+    this.contentElement.addStyleClass("source-code");
+    if (!WebInspector.settings.domWordWrap.get())
+        this.contentElement.classList.add("nowrap");
+    WebInspector.settings.domWordWrap.addChangeListener(this._domWordWrapSettingChanged.bind(this));
+
+    this.contentElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
+    this.splitView.sidebarElement.addEventListener("contextmenu", this._sidebarContextMenuEventFired.bind(this), false);
+
+    this.treeOutline = new WebInspector.ElementsTreeOutline(true, true, false, this._populateContextMenu.bind(this), this._setPseudoClassForNodeId.bind(this));
+    this.treeOutline.wireToDomAgent();
+
+    this.treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this);
+
+    this.crumbsElement = document.createElement("div");
+    this.crumbsElement.className = "crumbs";
+    this.crumbsElement.addEventListener("mousemove", this._mouseMovedInCrumbs.bind(this), false);
+    this.crumbsElement.addEventListener("mouseout", this._mouseMovedOutOfCrumbs.bind(this), false);
+
+    this.sidebarPanes = {};
+    this.sidebarPanes.computedStyle = new WebInspector.ComputedStyleSidebarPane();
+    this.sidebarPanes.styles = new WebInspector.StylesSidebarPane(this.sidebarPanes.computedStyle, this._setPseudoClassForNodeId.bind(this));
+    this.sidebarPanes.metrics = new WebInspector.MetricsSidebarPane();
+    this.sidebarPanes.properties = new WebInspector.PropertiesSidebarPane();
+    this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane.createProxy(this);
+    this.sidebarPanes.eventListeners = new WebInspector.EventListenersSidebarPane();
+
+    this.sidebarPanes.styles.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateStyles.bind(this, false));
+    this.sidebarPanes.metrics.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateMetrics.bind(this));
+    this.sidebarPanes.properties.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateProperties.bind(this));
+    this.sidebarPanes.eventListeners.addEventListener(WebInspector.SidebarPane.EventTypes.wasShown, this.updateEventListeners.bind(this));
+
+    this.sidebarPanes.styles.addEventListener("style edited", this._stylesPaneEdited, this);
+    this.sidebarPanes.styles.addEventListener("style property toggled", this._stylesPaneEdited, this);
+    this.sidebarPanes.metrics.addEventListener("metrics edited", this._metricsPaneEdited, this);
+
+    WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._dockSideChanged.bind(this));
+    WebInspector.settings.splitVerticallyWhenDockedToRight.addChangeListener(this._dockSideChanged.bind(this));
+    this._dockSideChanged();
+
+    this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this));
+    this._popoverHelper.setTimeout(0);
+
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._updateBreadcrumbIfNeeded, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrRemoved, this._updateBreadcrumbIfNeeded, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.NodeRemoved, this._nodeRemoved, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdatedEvent, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.InspectElementRequested, this._inspectElementRequested, this);
+
+    if (WebInspector.domAgent.existingDocument())
+        this._documentUpdated(WebInspector.domAgent.existingDocument());
+}
+
+WebInspector.ElementsPanel.prototype = {
+    get statusBarItems()
+    {
+        return [this.crumbsElement];
+    },
+
+    defaultFocusedElement: function()
+    {
+        return this.treeOutline.element;
+    },
+
+    statusBarResized: function()
+    {
+        this.updateBreadcrumbSizes();
+    },
+
+    wasShown: function()
+    {
+        // Attach heavy component lazily
+        if (this.treeOutline.element.parentElement !== this.contentElement)
+            this.contentElement.appendChild(this.treeOutline.element);
+
+        WebInspector.Panel.prototype.wasShown.call(this);
+
+        this.updateBreadcrumb();
+        this.treeOutline.updateSelection();
+        this.treeOutline.setVisible(true);
+
+        if (!this.treeOutline.rootDOMNode)
+            WebInspector.domAgent.requestDocument();
+    },
+
+    willHide: function()
+    {
+        WebInspector.domAgent.hideDOMNodeHighlight();
+        this.treeOutline.setVisible(false);
+        this._popoverHelper.hidePopover();
+
+        // Detach heavy component on hide
+        this.contentElement.removeChild(this.treeOutline.element);
+
+        WebInspector.Panel.prototype.willHide.call(this);
+    },
+
+    onResize: function()
+    {
+        this.treeOutline.updateSelection();
+        this.updateBreadcrumbSizes();
+    },
+
+    /**
+     * @param {DOMAgent.NodeId} nodeId
+     * @param {string} pseudoClass
+     * @param {boolean} enable
+     */
+    _setPseudoClassForNodeId: function(nodeId, pseudoClass, enable)
+    {
+        var node = WebInspector.domAgent.nodeForId(nodeId);
+        if (!node)
+            return;
+
+        var pseudoClasses = node.getUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
+        if (enable) {
+            pseudoClasses = pseudoClasses || [];
+            if (pseudoClasses.indexOf(pseudoClass) >= 0)
+                return;
+            pseudoClasses.push(pseudoClass);
+            node.setUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName, pseudoClasses);
+        } else {
+            if (!pseudoClasses || pseudoClasses.indexOf(pseudoClass) < 0)
+                return;
+            pseudoClasses.remove(pseudoClass);
+            if (!pseudoClasses.length)
+                node.removeUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
+        }
+
+        this.treeOutline.updateOpenCloseTags(node);
+        WebInspector.cssModel.forcePseudoState(node.id, node.getUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName));
+        this._metricsPaneEdited();
+        this._stylesPaneEdited();
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.ForcedElementState,
+            selector: node.appropriateSelectorFor(false),
+            enabled: enable,
+            state: pseudoClass
+        });
+    },
+
+    _selectedNodeChanged: function()
+    {
+        var selectedNode = this.selectedDOMNode();
+        if (!selectedNode && this._lastValidSelectedNode)
+            this._selectedPathOnReset = this._lastValidSelectedNode.path();
+
+        this.updateBreadcrumb(false);
+
+        this._updateSidebars();
+
+        if (selectedNode) {
+            ConsoleAgent.addInspectedNode(selectedNode.id);
+            this._lastValidSelectedNode = selectedNode;
+        }
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged);
+    },
+
+    _updateSidebars: function()
+    {
+        for (var pane in this.sidebarPanes)
+           this.sidebarPanes[pane].needsUpdate = true;
+
+        this.updateStyles(true);
+        this.updateMetrics();
+        this.updateProperties();
+        this.updateEventListeners();
+    },
+
+    _reset: function()
+    {
+        delete this.currentQuery;
+    },
+
+    _documentUpdatedEvent: function(event)
+    {
+        this._documentUpdated(event.data);
+    },
+
+    _documentUpdated: function(inspectedRootDocument)
+    {
+        this._reset();
+        this.searchCanceled();
+
+        this.treeOutline.rootDOMNode = inspectedRootDocument;
+
+        if (!inspectedRootDocument) {
+            if (this.isShowing())
+                WebInspector.domAgent.requestDocument();
+            return;
+        }
+
+        WebInspector.domBreakpointsSidebarPane.restoreBreakpoints();
+
+        /**
+         * @this {WebInspector.ElementsPanel}
+         * @param {WebInspector.DOMNode=} candidateFocusNode
+         */
+        function selectNode(candidateFocusNode)
+        {
+            if (!candidateFocusNode)
+                candidateFocusNode = inspectedRootDocument.body || inspectedRootDocument.documentElement;
+
+            if (!candidateFocusNode)
+                return;
+
+            this.selectDOMNode(candidateFocusNode);
+            if (this.treeOutline.selectedTreeElement)
+                this.treeOutline.selectedTreeElement.expand();
+        }
+
+        function selectLastSelectedNode(nodeId)
+        {
+            if (this.selectedDOMNode()) {
+                // Focused node has been explicitly set while reaching out for the last selected node.
+                return;
+            }
+            var node = nodeId ? WebInspector.domAgent.nodeForId(nodeId) : null;
+            selectNode.call(this, node);
+        }
+
+        if (this._selectedPathOnReset)
+            WebInspector.domAgent.pushNodeByPathToFrontend(this._selectedPathOnReset, selectLastSelectedNode.bind(this));
+        else
+            selectNode.call(this);
+        delete this._selectedPathOnReset;
+    },
+
+    searchCanceled: function()
+    {
+        delete this._searchQuery;
+        this._hideSearchHighlights();
+
+        WebInspector.searchController.updateSearchMatchesCount(0, this);
+
+        delete this._currentSearchResultIndex;
+        delete this._searchResults;
+        WebInspector.domAgent.cancelSearch();
+    },
+
+    /**
+     * @param {string} query
+     */
+    performSearch: function(query)
+    {
+        // Call searchCanceled since it will reset everything we need before doing a new search.
+        this.searchCanceled();
+
+        const whitespaceTrimmedQuery = query.trim();
+        if (!whitespaceTrimmedQuery.length)
+            return;
+
+        this._searchQuery = query;
+
+        /**
+         * @param {number} resultCount
+         */
+        function resultCountCallback(resultCount)
+        {
+            WebInspector.searchController.updateSearchMatchesCount(resultCount, this);
+            if (!resultCount)
+                return;
+
+            this._searchResults = new Array(resultCount);
+            this._currentSearchResultIndex = -1;
+            this.jumpToNextSearchResult();
+        }
+        WebInspector.domAgent.performSearch(whitespaceTrimmedQuery, resultCountCallback.bind(this));
+    },
+
+    _contextMenuEventFired: function(event)
+    {
+        function toggleWordWrap()
+        {
+            WebInspector.settings.domWordWrap.set(!WebInspector.settings.domWordWrap.get());
+        }
+
+        var contextMenu = new WebInspector.ContextMenu(event);
+        this.treeOutline.populateContextMenu(contextMenu, event);
+
+        if (WebInspector.experimentsSettings.cssRegions.isEnabled()) {
+            contextMenu.appendSeparator();
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "CSS named flows\u2026" : "CSS Named Flows\u2026"), this._showNamedFlowCollections.bind(this));
+        }
+
+        contextMenu.appendSeparator();
+        contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Word wrap" : "Word Wrap"), toggleWordWrap.bind(this), WebInspector.settings.domWordWrap.get());
+
+        contextMenu.show();
+    },
+
+    _showNamedFlowCollections: function()
+    {
+        if (!WebInspector.cssNamedFlowCollectionsView)
+            WebInspector.cssNamedFlowCollectionsView = new WebInspector.CSSNamedFlowCollectionsView();
+        WebInspector.cssNamedFlowCollectionsView.showInDrawer();
+    },
+
+    _domWordWrapSettingChanged: function(event)
+    {
+        if (event.data)
+            this.contentElement.removeStyleClass("nowrap");
+        else
+            this.contentElement.addStyleClass("nowrap");
+
+        var selectedNode = this.selectedDOMNode();
+        if (!selectedNode)
+            return;
+
+        var treeElement = this.treeOutline.findTreeElement(selectedNode);
+        if (treeElement)
+            treeElement.updateSelection(); // Recalculate selection highlight dimensions.
+    },
+
+    switchToAndFocus: function(node)
+    {
+        // Reset search restore.
+        WebInspector.searchController.cancelSearch();
+        WebInspector.inspectorView.setCurrentPanel(this);
+        this.selectDOMNode(node, true);
+    },
+
+    _populateContextMenu: function(contextMenu, node)
+    {
+        // Add debbuging-related actions
+        contextMenu.appendSeparator();
+        var pane = WebInspector.domBreakpointsSidebarPane;
+        pane.populateNodeContextMenu(node, contextMenu);
+    },
+
+    _getPopoverAnchor: function(element)
+    {
+        var anchor = element.enclosingNodeOrSelfWithClass("webkit-html-resource-link");
+        if (anchor) {
+            if (!anchor.href)
+                return null;
+
+            var resource = WebInspector.resourceTreeModel.resourceForURL(anchor.href);
+            if (!resource || resource.type !== WebInspector.resourceTypes.Image)
+                return null;
+
+            anchor.removeAttribute("title");
+        }
+        return anchor;
+    },
+    
+    _loadDimensionsForNode: function(treeElement, callback)
+    {
+        // We get here for CSS properties, too, so bail out early for non-DOM treeElements.
+        if (treeElement.treeOutline !== this.treeOutline) {
+            callback();
+            return;
+        }
+        
+        var node = /** @type {WebInspector.DOMNode} */ (treeElement.representedObject);
+
+        if (!node.nodeName() || node.nodeName().toLowerCase() !== "img") {
+            callback();
+            return;
+        }
+
+        WebInspector.RemoteObject.resolveNode(node, "", resolvedNode);
+
+        function resolvedNode(object)
+        {
+            if (!object) {
+                callback();
+                return;
+            }
+
+            object.callFunctionJSON(dimensions, undefined, callback);
+            object.release();
+
+            function dimensions()
+            {
+                return { offsetWidth: this.offsetWidth, offsetHeight: this.offsetHeight, naturalWidth: this.naturalWidth, naturalHeight: this.naturalHeight };
+            }
+        }
+    },
+
+    /**
+     * @param {Element} anchor
+     * @param {WebInspector.Popover} popover
+     */
+    _showPopover: function(anchor, popover)
+    {
+        var listItem = anchor.enclosingNodeOrSelfWithNodeName("li");
+        if (listItem && listItem.treeElement)
+            this._loadDimensionsForNode(listItem.treeElement, WebInspector.DOMPresentationUtils.buildImagePreviewContents.bind(WebInspector.DOMPresentationUtils, anchor.href, true, showPopover));
+        else
+            WebInspector.DOMPresentationUtils.buildImagePreviewContents(anchor.href, true, showPopover);
+
+        /**
+         * @param {Element=} contents
+         */
+        function showPopover(contents)
+        {
+            if (!contents)
+                return;
+            popover.setCanShrink(false);
+            popover.show(contents, anchor);
+        }
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        if (!this._searchResults)
+            return;
+
+        this._hideSearchHighlights();
+        if (++this._currentSearchResultIndex >= this._searchResults.length)
+            this._currentSearchResultIndex = 0;
+
+        this._highlightCurrentSearchResult();
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        if (!this._searchResults)
+            return;
+
+        this._hideSearchHighlights();
+        if (--this._currentSearchResultIndex < 0)
+            this._currentSearchResultIndex = (this._searchResults.length - 1);
+
+        this._highlightCurrentSearchResult();
+    },
+
+    _highlightCurrentSearchResult: function()
+    {
+        var index = this._currentSearchResultIndex;
+        var searchResults = this._searchResults;
+        var searchResult = searchResults[index];
+
+        if (searchResult === null) {
+            WebInspector.searchController.updateCurrentMatchIndex(index, this);
+            return;
+        }
+
+        if (typeof searchResult === "undefined") {
+            // No data for slot, request it.
+            function callback(node)
+            {
+                searchResults[index] = node || null;
+                this._highlightCurrentSearchResult();
+            }
+            WebInspector.domAgent.searchResult(index, callback.bind(this));
+            return;
+        }
+
+        WebInspector.searchController.updateCurrentMatchIndex(index, this);
+
+        var treeElement = this.treeOutline.findTreeElement(searchResult);
+        if (treeElement) {
+            treeElement.highlightSearchResults(this._searchQuery);
+            treeElement.reveal();
+            var matches = treeElement.listItemElement.getElementsByClassName("webkit-search-result");
+            if (matches.length)
+                matches[0].scrollIntoViewIfNeeded();
+        }
+    },
+
+    _hideSearchHighlights: function()
+    {
+        if (!this._searchResults)
+            return;
+        var searchResult = this._searchResults[this._currentSearchResultIndex];
+        if (!searchResult)
+            return;
+        var treeElement = this.treeOutline.findTreeElement(searchResult);
+        if (treeElement)
+            treeElement.hideSearchHighlights();
+    },
+
+    selectedDOMNode: function()
+    {
+        return this.treeOutline.selectedDOMNode();
+    },
+
+    /**
+     * @param {boolean=} focus
+     */
+    selectDOMNode: function(node, focus)
+    {
+        this.treeOutline.selectDOMNode(node, focus);
+    },
+
+    _nodeRemoved: function(event)
+    {
+        if (!this.isShowing())
+            return;
+
+        var crumbs = this.crumbsElement;
+        for (var crumb = crumbs.firstChild; crumb; crumb = crumb.nextSibling) {
+            if (crumb.representedObject === event.data.node) {
+                this.updateBreadcrumb(true);
+                return;
+            }
+        }
+    },
+
+    _stylesPaneEdited: function()
+    {
+        // Once styles are edited, the Metrics pane should be updated.
+        this.sidebarPanes.metrics.needsUpdate = true;
+        this.updateMetrics();
+    },
+
+    _metricsPaneEdited: function()
+    {
+        // Once metrics are edited, the Styles pane should be updated.
+        this.sidebarPanes.styles.needsUpdate = true;
+        this.updateStyles(true);
+    },
+
+    _mouseMovedInCrumbs: function(event)
+    {
+        var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
+        var crumbElement = nodeUnderMouse.enclosingNodeOrSelfWithClass("crumb");
+
+        WebInspector.domAgent.highlightDOMNode(crumbElement ? crumbElement.representedObject.id : 0);
+
+        if ("_mouseOutOfCrumbsTimeout" in this) {
+            clearTimeout(this._mouseOutOfCrumbsTimeout);
+            delete this._mouseOutOfCrumbsTimeout;
+        }
+    },
+
+    _mouseMovedOutOfCrumbs: function(event)
+    {
+        var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
+        if (nodeUnderMouse && nodeUnderMouse.isDescendant(this.crumbsElement))
+            return;
+
+        WebInspector.domAgent.hideDOMNodeHighlight();
+
+        this._mouseOutOfCrumbsTimeout = setTimeout(this.updateBreadcrumbSizes.bind(this), 1000);
+    },
+
+    _updateBreadcrumbIfNeeded: function(event)
+    {
+        var name = event.data.name;
+        if (name !== "class" && name !== "id")
+            return;
+
+        var node = /** @type {WebInspector.DOMNode} */ (event.data.node);
+        var crumbs = this.crumbsElement;
+        var crumb = crumbs.firstChild;
+        while (crumb) {
+            if (crumb.representedObject === node) {
+                this.updateBreadcrumb(true);
+                break;
+            }
+            crumb = crumb.nextSibling;
+        }
+    },
+
+    /**
+     * @param {boolean=} forceUpdate
+     */
+    updateBreadcrumb: function(forceUpdate)
+    {
+        if (!this.isShowing())
+            return;
+
+        var crumbs = this.crumbsElement;
+
+        var handled = false;
+        var crumb = crumbs.firstChild;
+        while (crumb) {
+            if (crumb.representedObject === this.selectedDOMNode()) {
+                crumb.addStyleClass("selected");
+                handled = true;
+            } else {
+                crumb.removeStyleClass("selected");
+            }
+
+            crumb = crumb.nextSibling;
+        }
+
+        if (handled && !forceUpdate) {
+            // We don't need to rebuild the crumbs, but we need to adjust sizes
+            // to reflect the new focused or root node.
+            this.updateBreadcrumbSizes();
+            return;
+        }
+
+        crumbs.removeChildren();
+
+        var panel = this;
+
+        function selectCrumbFunction(event)
+        {
+            var crumb = event.currentTarget;
+            if (crumb.hasStyleClass("collapsed")) {
+                // Clicking a collapsed crumb will expose the hidden crumbs.
+                if (crumb === panel.crumbsElement.firstChild) {
+                    // If the focused crumb is the first child, pick the farthest crumb
+                    // that is still hidden. This allows the user to expose every crumb.
+                    var currentCrumb = crumb;
+                    while (currentCrumb) {
+                        var hidden = currentCrumb.hasStyleClass("hidden");
+                        var collapsed = currentCrumb.hasStyleClass("collapsed");
+                        if (!hidden && !collapsed)
+                            break;
+                        crumb = currentCrumb;
+                        currentCrumb = currentCrumb.nextSibling;
+                    }
+                }
+
+                panel.updateBreadcrumbSizes(crumb);
+            } else
+                panel.selectDOMNode(crumb.representedObject, true);
+
+            event.preventDefault();
+        }
+
+        for (var current = this.selectedDOMNode(); current; current = current.parentNode) {
+            if (current.nodeType() === Node.DOCUMENT_NODE)
+                continue;
+
+            crumb = document.createElement("span");
+            crumb.className = "crumb";
+            crumb.representedObject = current;
+            crumb.addEventListener("mousedown", selectCrumbFunction, false);
+
+            var crumbTitle;
+            switch (current.nodeType()) {
+                case Node.ELEMENT_NODE:
+                    WebInspector.DOMPresentationUtils.decorateNodeLabel(current, crumb);
+                    break;
+
+                case Node.TEXT_NODE:
+                    crumbTitle = WebInspector.UIString("(text)");
+                    break
+
+                case Node.COMMENT_NODE:
+                    crumbTitle = "<!-->";
+                    break;
+
+                case Node.DOCUMENT_TYPE_NODE:
+                    crumbTitle = "<!DOCTYPE>";
+                    break;
+
+                default:
+                    crumbTitle = current.nodeNameInCorrectCase();
+            }
+
+            if (!crumb.childNodes.length) {
+                var nameElement = document.createElement("span");
+                nameElement.textContent = crumbTitle;
+                crumb.appendChild(nameElement);
+                crumb.title = crumbTitle;
+            }
+
+            if (current === this.selectedDOMNode())
+                crumb.addStyleClass("selected");
+            if (!crumbs.childNodes.length)
+                crumb.addStyleClass("end");
+
+            crumbs.appendChild(crumb);
+        }
+
+        if (crumbs.hasChildNodes())
+            crumbs.lastChild.addStyleClass("start");
+
+        this.updateBreadcrumbSizes();
+    },
+
+    /**
+     * @param {Element=} focusedCrumb
+     */
+    updateBreadcrumbSizes: function(focusedCrumb)
+    {
+        if (!this.isShowing())
+            return;
+
+        if (document.body.offsetWidth <= 0) {
+            // The stylesheet hasn't loaded yet or the window is closed,
+            // so we can't calculate what is need. Return early.
+            return;
+        }
+
+        var crumbs = this.crumbsElement;
+        if (!crumbs.childNodes.length || crumbs.offsetWidth <= 0)
+            return; // No crumbs, do nothing.
+
+        // A Zero index is the right most child crumb in the breadcrumb.
+        var selectedIndex = 0;
+        var focusedIndex = 0;
+        var selectedCrumb;
+
+        var i = 0;
+        var crumb = crumbs.firstChild;
+        while (crumb) {
+            // Find the selected crumb and index.
+            if (!selectedCrumb && crumb.hasStyleClass("selected")) {
+                selectedCrumb = crumb;
+                selectedIndex = i;
+            }
+
+            // Find the focused crumb index.
+            if (crumb === focusedCrumb)
+                focusedIndex = i;
+
+            // Remove any styles that affect size before
+            // deciding to shorten any crumbs.
+            if (crumb !== crumbs.lastChild)
+                crumb.removeStyleClass("start");
+            if (crumb !== crumbs.firstChild)
+                crumb.removeStyleClass("end");
+
+            crumb.removeStyleClass("compact");
+            crumb.removeStyleClass("collapsed");
+            crumb.removeStyleClass("hidden");
+
+            crumb = crumb.nextSibling;
+            ++i;
+        }
+
+        // Restore the start and end crumb classes in case they got removed in coalesceCollapsedCrumbs().
+        // The order of the crumbs in the document is opposite of the visual order.
+        crumbs.firstChild.addStyleClass("end");
+        crumbs.lastChild.addStyleClass("start");
+
+        function crumbsAreSmallerThanContainer()
+        {
+            var rightPadding = 20;
+            var errorWarningElement = document.getElementById("error-warning-count");
+            if (!WebInspector.drawer.visible && errorWarningElement)
+                rightPadding += errorWarningElement.offsetWidth;
+            return ((crumbs.totalOffsetLeft() + crumbs.offsetWidth + rightPadding) < window.innerWidth);
+        }
+
+        if (crumbsAreSmallerThanContainer())
+            return; // No need to compact the crumbs, they all fit at full size.
+
+        var BothSides = 0;
+        var AncestorSide = -1;
+        var ChildSide = 1;
+
+        /**
+         * @param {boolean=} significantCrumb
+         */
+        function makeCrumbsSmaller(shrinkingFunction, direction, significantCrumb)
+        {
+            if (!significantCrumb)
+                significantCrumb = (focusedCrumb || selectedCrumb);
+
+            if (significantCrumb === selectedCrumb)
+                var significantIndex = selectedIndex;
+            else if (significantCrumb === focusedCrumb)
+                var significantIndex = focusedIndex;
+            else {
+                var significantIndex = 0;
+                for (var i = 0; i < crumbs.childNodes.length; ++i) {
+                    if (crumbs.childNodes[i] === significantCrumb) {
+                        significantIndex = i;
+                        break;
+                    }
+                }
+            }
+
+            function shrinkCrumbAtIndex(index)
+            {
+                var shrinkCrumb = crumbs.childNodes[index];
+                if (shrinkCrumb && shrinkCrumb !== significantCrumb)
+                    shrinkingFunction(shrinkCrumb);
+                if (crumbsAreSmallerThanContainer())
+                    return true; // No need to compact the crumbs more.
+                return false;
+            }
+
+            // Shrink crumbs one at a time by applying the shrinkingFunction until the crumbs
+            // fit in the container or we run out of crumbs to shrink.
+            if (direction) {
+                // Crumbs are shrunk on only one side (based on direction) of the signifcant crumb.
+                var index = (direction > 0 ? 0 : crumbs.childNodes.length - 1);
+                while (index !== significantIndex) {
+                    if (shrinkCrumbAtIndex(index))
+                        return true;
+                    index += (direction > 0 ? 1 : -1);
+                }
+            } else {
+                // Crumbs are shrunk in order of descending distance from the signifcant crumb,
+                // with a tie going to child crumbs.
+                var startIndex = 0;
+                var endIndex = crumbs.childNodes.length - 1;
+                while (startIndex != significantIndex || endIndex != significantIndex) {
+                    var startDistance = significantIndex - startIndex;
+                    var endDistance = endIndex - significantIndex;
+                    if (startDistance >= endDistance)
+                        var index = startIndex++;
+                    else
+                        var index = endIndex--;
+                    if (shrinkCrumbAtIndex(index))
+                        return true;
+                }
+            }
+
+            // We are not small enough yet, return false so the caller knows.
+            return false;
+        }
+
+        function coalesceCollapsedCrumbs()
+        {
+            var crumb = crumbs.firstChild;
+            var collapsedRun = false;
+            var newStartNeeded = false;
+            var newEndNeeded = false;
+            while (crumb) {
+                var hidden = crumb.hasStyleClass("hidden");
+                if (!hidden) {
+                    var collapsed = crumb.hasStyleClass("collapsed");
+                    if (collapsedRun && collapsed) {
+                        crumb.addStyleClass("hidden");
+                        crumb.removeStyleClass("compact");
+                        crumb.removeStyleClass("collapsed");
+
+                        if (crumb.hasStyleClass("start")) {
+                            crumb.removeStyleClass("start");
+                            newStartNeeded = true;
+                        }
+
+                        if (crumb.hasStyleClass("end")) {
+                            crumb.removeStyleClass("end");
+                            newEndNeeded = true;
+                        }
+
+                        continue;
+                    }
+
+                    collapsedRun = collapsed;
+
+                    if (newEndNeeded) {
+                        newEndNeeded = false;
+                        crumb.addStyleClass("end");
+                    }
+                } else
+                    collapsedRun = true;
+                crumb = crumb.nextSibling;
+            }
+
+            if (newStartNeeded) {
+                crumb = crumbs.lastChild;
+                while (crumb) {
+                    if (!crumb.hasStyleClass("hidden")) {
+                        crumb.addStyleClass("start");
+                        break;
+                    }
+                    crumb = crumb.previousSibling;
+                }
+            }
+        }
+
+        function compact(crumb)
+        {
+            if (crumb.hasStyleClass("hidden"))
+                return;
+            crumb.addStyleClass("compact");
+        }
+
+        function collapse(crumb, dontCoalesce)
+        {
+            if (crumb.hasStyleClass("hidden"))
+                return;
+            crumb.addStyleClass("collapsed");
+            crumb.removeStyleClass("compact");
+            if (!dontCoalesce)
+                coalesceCollapsedCrumbs();
+        }
+
+        if (!focusedCrumb) {
+            // When not focused on a crumb we can be biased and collapse less important
+            // crumbs that the user might not care much about.
+
+            // Compact child crumbs.
+            if (makeCrumbsSmaller(compact, ChildSide))
+                return;
+
+            // Collapse child crumbs.
+            if (makeCrumbsSmaller(collapse, ChildSide))
+                return;
+        }
+
+        // Compact ancestor crumbs, or from both sides if focused.
+        if (makeCrumbsSmaller(compact, (focusedCrumb ? BothSides : AncestorSide)))
+            return;
+
+        // Collapse ancestor crumbs, or from both sides if focused.
+        if (makeCrumbsSmaller(collapse, (focusedCrumb ? BothSides : AncestorSide)))
+            return;
+
+        if (!selectedCrumb)
+            return;
+
+        // Compact the selected crumb.
+        compact(selectedCrumb);
+        if (crumbsAreSmallerThanContainer())
+            return;
+
+        // Collapse the selected crumb as a last resort. Pass true to prevent coalescing.
+        collapse(selectedCrumb, true);
+    },
+
+    /**
+     * @param {boolean=} forceUpdate
+     */
+    updateStyles: function(forceUpdate)
+    {
+        var stylesSidebarPane = this.sidebarPanes.styles;
+        var computedStylePane = this.sidebarPanes.computedStyle;
+        if ((!stylesSidebarPane.isShowing() && !computedStylePane.isShowing()) || !stylesSidebarPane.needsUpdate)
+            return;
+
+        stylesSidebarPane.update(this.selectedDOMNode(), forceUpdate);
+        stylesSidebarPane.needsUpdate = false;
+    },
+
+    updateMetrics: function()
+    {
+        var metricsSidebarPane = this.sidebarPanes.metrics;
+        if (!metricsSidebarPane.isShowing() || !metricsSidebarPane.needsUpdate)
+            return;
+
+        metricsSidebarPane.update(this.selectedDOMNode());
+        metricsSidebarPane.needsUpdate = false;
+    },
+
+    updateProperties: function()
+    {
+        var propertiesSidebarPane = this.sidebarPanes.properties;
+        if (!propertiesSidebarPane.isShowing() || !propertiesSidebarPane.needsUpdate)
+            return;
+
+        propertiesSidebarPane.update(this.selectedDOMNode());
+        propertiesSidebarPane.needsUpdate = false;
+    },
+
+    updateEventListeners: function()
+    {
+        var eventListenersSidebarPane = this.sidebarPanes.eventListeners;
+        if (!eventListenersSidebarPane.isShowing() || !eventListenersSidebarPane.needsUpdate)
+            return;
+
+        eventListenersSidebarPane.update(this.selectedDOMNode());
+        eventListenersSidebarPane.needsUpdate = false;
+    },
+
+    handleShortcut: function(event)
+    {
+        function handleUndoRedo()
+        {
+            if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && !event.shiftKey && event.keyIdentifier === "U+005A") { // Z key
+                WebInspector.domAgent.undo(this._updateSidebars.bind(this));
+                event.handled = true;
+                return;
+            }
+
+            var isRedoKey = WebInspector.isMac() ? event.metaKey && event.shiftKey && event.keyIdentifier === "U+005A" : // Z key
+                                                   event.ctrlKey && event.keyIdentifier === "U+0059"; // Y key
+            if (isRedoKey) {
+                DOMAgent.redo(this._updateSidebars.bind(this));
+                event.handled = true;
+            }
+        }
+
+        if (!this.treeOutline.editing()) {
+            handleUndoRedo.call(this);
+            if (event.handled)
+                return;
+        }
+
+        this.treeOutline.handleShortcut(event);
+    },
+
+    handleCopyEvent: function(event)
+    {
+        // Don't prevent the normal copy if the user has a selection.
+        if (!window.getSelection().isCollapsed)
+            return;
+        event.clipboardData.clearData();
+        event.preventDefault();
+        this.selectedDOMNode().copyNode();
+    },
+
+    sidebarResized: function(event)
+    {
+        this.treeOutline.updateSelection();
+    },
+
+    _inspectElementRequested: function(event)
+    {
+        var node = event.data;
+        this.revealAndSelectNode(node.id);
+    },
+
+    revealAndSelectNode: function(nodeId)
+    {
+        WebInspector.inspectorView.setCurrentPanel(this);
+
+        var node = WebInspector.domAgent.nodeForId(nodeId);
+        if (!node)
+            return;
+
+        WebInspector.domAgent.highlightDOMNodeForTwoSeconds(nodeId);
+        this.selectDOMNode(node, true);
+    },
+
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    appendApplicableItems: function(event, contextMenu, target)
+    {
+        if (!(target instanceof WebInspector.RemoteObject))
+            return;
+        var remoteObject = /** @type {WebInspector.RemoteObject} */ (target);
+        if (remoteObject.subtype !== "node")
+            return;
+
+        function selectNode(nodeId)
+        {
+            if (nodeId)
+                WebInspector.domAgent.inspectElement(nodeId);
+        }
+  
+        function revealElement()
+        {
+            remoteObject.pushNodeToFrontend(selectNode);
+        }
+
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Elements panel" : "Reveal in Elements Panel"), revealElement.bind(this));
+    },
+
+    _sidebarContextMenuEventFired: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.show();
+    },
+
+    _dockSideChanged: function()
+    {
+        var dockSide = WebInspector.dockController.dockSide();
+        var vertically = dockSide === WebInspector.DockController.State.DockedToRight && WebInspector.settings.splitVerticallyWhenDockedToRight.get();
+        this._splitVertically(vertically);
+    },
+
+    /**
+     * @param {boolean} vertically
+     */
+    _splitVertically: function(vertically)
+    {
+        if (this.sidebarPaneView && vertically === !this.splitView.isVertical())
+            return;
+
+        if (this.sidebarPaneView)
+            this.sidebarPaneView.detach();
+
+        this.splitView.setVertical(!vertically);
+
+        if (!vertically) {
+            this.sidebarPaneView = new WebInspector.SidebarPaneStack();
+            for (var pane in this.sidebarPanes)
+                this.sidebarPaneView.addPane(this.sidebarPanes[pane]);
+        } else {
+            this.sidebarPaneView = new WebInspector.SidebarTabbedPane();
+
+            var compositePane = new WebInspector.SidebarPane(this.sidebarPanes.styles.title());
+            compositePane.element.addStyleClass("composite");
+            compositePane.element.addStyleClass("fill");
+            var expandComposite = compositePane.expand.bind(compositePane);
+
+            var splitView = new WebInspector.SplitView(true, "StylesPaneSplitRatio", 0.5);
+            splitView.show(compositePane.bodyElement);
+
+            this.sidebarPanes.styles.show(splitView.firstElement());
+            splitView.firstElement().appendChild(this.sidebarPanes.styles.titleElement);
+            this.sidebarPanes.styles.setExpandCallback(expandComposite);
+
+            this.sidebarPanes.metrics.show(splitView.secondElement());
+            this.sidebarPanes.metrics.setExpandCallback(expandComposite);
+
+            splitView.secondElement().appendChild(this.sidebarPanes.computedStyle.titleElement);
+            splitView.secondElement().addStyleClass("metrics-and-computed");
+            this.sidebarPanes.computedStyle.show(splitView.secondElement());
+            this.sidebarPanes.computedStyle.setExpandCallback(expandComposite);
+
+            this.sidebarPaneView.addPane(compositePane);
+            this.sidebarPaneView.addPane(this.sidebarPanes.properties);
+            this.sidebarPaneView.addPane(this.sidebarPanes.domBreakpoints);
+            this.sidebarPaneView.addPane(this.sidebarPanes.eventListeners);
+        }
+        this.sidebarPaneView.show(this.splitView.sidebarElement);
+        this.sidebarPanes.styles.expand();
+    },
+
+    /**
+     * @param {string} id
+     * @param {WebInspector.SidebarPane} pane
+     */
+    addExtensionSidebarPane: function(id, pane)
+    {
+        this.sidebarPanes[id] = pane;
+        this.sidebarPaneView.addPane(pane);
+    },
+
+    __proto__: WebInspector.Panel.prototype
+}
diff --git a/Source/devtools/front_end/ElementsPanelDescriptor.js b/Source/devtools/front_end/ElementsPanelDescriptor.js
new file mode 100644
index 0000000..2cec5e6
--- /dev/null
+++ b/Source/devtools/front_end/ElementsPanelDescriptor.js
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.PanelDescriptor}
+ * @implements {WebInspector.ContextMenu.Provider}
+ */
+WebInspector.ElementsPanelDescriptor = function()
+{
+    WebInspector.PanelDescriptor.call(this, "elements", WebInspector.UIString("Elements"), "ElementsPanel", "ElementsPanel.js");
+    WebInspector.ContextMenu.registerProvider(this);
+}
+
+WebInspector.ElementsPanelDescriptor.prototype = {
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    appendApplicableItems: function(event, contextMenu, target)
+    {
+        if (!(target instanceof WebInspector.RemoteObject))
+            return;
+        var remoteObject = /** @type {WebInspector.RemoteObject} */ (target);
+        if (remoteObject.subtype !== "node")
+            return;
+        this.panel().appendApplicableItems(event, contextMenu, target);
+    },
+
+    registerShortcuts: function()
+    {
+        var elementsSection = WebInspector.shortcutsScreen.section(WebInspector.UIString("Elements Panel"));
+
+        var navigate = WebInspector.ElementsPanelDescriptor.ShortcutKeys.NavigateUp.concat(WebInspector.ElementsPanelDescriptor.ShortcutKeys.NavigateDown);
+        elementsSection.addRelatedKeys(navigate, WebInspector.UIString("Navigate elements"));
+
+        var expandCollapse = WebInspector.ElementsPanelDescriptor.ShortcutKeys.Expand.concat(WebInspector.ElementsPanelDescriptor.ShortcutKeys.Collapse);
+        elementsSection.addRelatedKeys(expandCollapse, WebInspector.UIString("Expand/collapse"));
+
+        elementsSection.addAlternateKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.EditAttribute, WebInspector.UIString("Edit attribute"));
+        elementsSection.addAlternateKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.HideElement, WebInspector.UIString("Hide element"));
+        elementsSection.addAlternateKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.ToggleEditAsHTML, WebInspector.UIString("Toggle edit as HTML"));
+
+        var stylesPaneSection = WebInspector.shortcutsScreen.section(WebInspector.UIString("Styles Pane"));
+
+        var nextPreviousProperty = WebInspector.ElementsPanelDescriptor.ShortcutKeys.NextProperty.concat(WebInspector.ElementsPanelDescriptor.ShortcutKeys.PreviousProperty);
+        stylesPaneSection.addRelatedKeys(nextPreviousProperty, WebInspector.UIString("Next/previous property"));
+
+        stylesPaneSection.addRelatedKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.IncrementValue, WebInspector.UIString("Increment value"));
+        stylesPaneSection.addRelatedKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.DecrementValue, WebInspector.UIString("Decrement value"));
+
+        stylesPaneSection.addAlternateKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.IncrementBy10, WebInspector.UIString("Increment by %f", 10));
+        stylesPaneSection.addAlternateKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.DecrementBy10, WebInspector.UIString("Decrement by %f", 10));
+
+        stylesPaneSection.addAlternateKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.IncrementBy100, WebInspector.UIString("Increment by %f", 100));
+        stylesPaneSection.addAlternateKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.DecrementBy100, WebInspector.UIString("Decrement by %f", 100));
+
+        stylesPaneSection.addAlternateKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.IncrementBy01, WebInspector.UIString("Increment by %f", 0.1));
+        stylesPaneSection.addAlternateKeys(WebInspector.ElementsPanelDescriptor.ShortcutKeys.DecrementBy01, WebInspector.UIString("Decrement by %f", 0.1));
+    },
+
+    __proto__: WebInspector.PanelDescriptor.prototype
+}
+
+WebInspector.ElementsPanelDescriptor.ShortcutKeys = {
+    NavigateUp: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Up)
+    ],
+
+    NavigateDown: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Down)
+    ],
+
+    Expand: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Right)
+    ],
+
+    Collapse: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Left)
+    ],
+
+    EditAttribute: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Enter)
+    ],
+
+    HideElement: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.H)
+    ],
+
+    ToggleEditAsHTML: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F2)
+    ],
+
+    NextProperty: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Tab)
+    ],
+
+    PreviousProperty: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Tab, WebInspector.KeyboardShortcut.Modifiers.Shift)
+    ],
+
+    IncrementValue: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Up)
+    ],
+
+    DecrementValue: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Down)
+    ],
+
+    IncrementBy10: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.PageUp),
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Up, WebInspector.KeyboardShortcut.Modifiers.Shift)
+    ],
+
+    DecrementBy10: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.PageDown),
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Down, WebInspector.KeyboardShortcut.Modifiers.Shift)
+    ],
+
+    IncrementBy100: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.PageUp, WebInspector.KeyboardShortcut.Modifiers.Shift)
+    ],
+
+    DecrementBy100: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.PageDown, WebInspector.KeyboardShortcut.Modifiers.Shift)
+    ],
+
+    IncrementBy01: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.PageUp, WebInspector.KeyboardShortcut.Modifiers.Alt)
+    ],
+
+    DecrementBy01: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.PageDown, WebInspector.KeyboardShortcut.Modifiers.Alt)
+    ]
+};
diff --git a/Source/devtools/front_end/ElementsTreeOutline.js b/Source/devtools/front_end/ElementsTreeOutline.js
new file mode 100755
index 0000000..95d927f
--- /dev/null
+++ b/Source/devtools/front_end/ElementsTreeOutline.js
@@ -0,0 +1,2280 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
+ * 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 Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {TreeOutline}
+ * @param {boolean=} omitRootDOMNode
+ * @param {boolean=} selectEnabled
+ * @param {boolean=} showInElementsPanelEnabled
+ * @param {function(WebInspector.ContextMenu, WebInspector.DOMNode)=} contextMenuCallback
+ * @param {function(DOMAgent.NodeId, string, boolean)=} setPseudoClassCallback
+ */
+WebInspector.ElementsTreeOutline = function(omitRootDOMNode, selectEnabled, showInElementsPanelEnabled, contextMenuCallback, setPseudoClassCallback)
+{
+    this.element = document.createElement("ol");
+    this.element.className = "elements-tree-outline";
+    this.element.addEventListener("mousedown", this._onmousedown.bind(this), false);
+    this.element.addEventListener("mousemove", this._onmousemove.bind(this), false);
+    this.element.addEventListener("mouseout", this._onmouseout.bind(this), false);
+    this.element.addEventListener("dragstart", this._ondragstart.bind(this), false);
+    this.element.addEventListener("dragover", this._ondragover.bind(this), false);
+    this.element.addEventListener("dragleave", this._ondragleave.bind(this), false);
+    this.element.addEventListener("drop", this._ondrop.bind(this), false);
+    this.element.addEventListener("dragend", this._ondragend.bind(this), false);
+    this.element.addEventListener("keydown", this._onkeydown.bind(this), false);
+
+    TreeOutline.call(this, this.element);
+
+    this._includeRootDOMNode = !omitRootDOMNode;
+    this._selectEnabled = selectEnabled;
+    this._showInElementsPanelEnabled = showInElementsPanelEnabled;
+    this._rootDOMNode = null;
+    this._selectDOMNode = null;
+    this._eventSupport = new WebInspector.Object();
+
+    this._visible = false;
+
+    this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
+    this._contextMenuCallback = contextMenuCallback;
+    this._setPseudoClassCallback = setPseudoClassCallback;
+    this._createNodeDecorators();
+}
+
+WebInspector.ElementsTreeOutline.Events = {
+    SelectedNodeChanged: "SelectedNodeChanged"
+}
+
+WebInspector.ElementsTreeOutline.MappedCharToEntity = {
+    "\u00a0": "nbsp",
+    "\u2002": "ensp",
+    "\u2003": "emsp",
+    "\u2009": "thinsp",
+    "\u200a": "#8202", // Hairspace
+    "\u200b": "#8203", // ZWSP
+    "\u200c": "zwnj",
+    "\u200d": "zwj",
+    "\u200e": "lrm",
+    "\u200f": "rlm",
+    "\u202a": "#8234", // LRE
+    "\u202b": "#8235", // RLE
+    "\u202c": "#8236", // PDF
+    "\u202d": "#8237", // LRO
+    "\u202e": "#8238" // RLO
+}
+
+WebInspector.ElementsTreeOutline.prototype = {
+    _createNodeDecorators: function()
+    {
+        this._nodeDecorators = [];
+        this._nodeDecorators.push(new WebInspector.ElementsTreeOutline.PseudoStateDecorator());
+    },
+
+    wireToDomAgent: function()
+    {
+        this._elementsTreeUpdater = new WebInspector.ElementsTreeUpdater(this);
+    },
+
+    setVisible: function(visible)
+    {
+        this._visible = visible;
+        if (!this._visible)
+            return;
+
+        this._updateModifiedNodes();
+        if (this._selectedDOMNode)
+            this._revealAndSelectNode(this._selectedDOMNode, false);
+    },
+
+    addEventListener: function(eventType, listener, thisObject)
+    {
+        this._eventSupport.addEventListener(eventType, listener, thisObject);
+    },
+
+    removeEventListener: function(eventType, listener, thisObject)
+    {
+        this._eventSupport.removeEventListener(eventType, listener, thisObject);
+    },
+
+    get rootDOMNode()
+    {
+        return this._rootDOMNode;
+    },
+
+    set rootDOMNode(x)
+    {
+        if (this._rootDOMNode === x)
+            return;
+
+        this._rootDOMNode = x;
+
+        this._isXMLMimeType = x && x.isXMLNode();
+
+        this.update();
+    },
+
+    get isXMLMimeType()
+    {
+        return this._isXMLMimeType;
+    },
+
+    selectedDOMNode: function()
+    {
+        return this._selectedDOMNode;
+    },
+
+    selectDOMNode: function(node, focus)
+    {
+        if (this._selectedDOMNode === node) {
+            this._revealAndSelectNode(node, !focus);
+            return;
+        }
+
+        this._selectedDOMNode = node;
+        this._revealAndSelectNode(node, !focus);
+
+        // The _revealAndSelectNode() method might find a different element if there is inlined text,
+        // and the select() call would change the selectedDOMNode and reenter this setter. So to
+        // avoid calling _selectedNodeChanged() twice, first check if _selectedDOMNode is the same
+        // node as the one passed in.
+        if (this._selectedDOMNode === node)
+            this._selectedNodeChanged();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    editing: function()
+    {
+        var node = this.selectedDOMNode();
+        if (!node)
+            return false;
+        var treeElement = this.findTreeElement(node);
+        if (!treeElement)
+            return false;
+        return treeElement._editing || false;
+    },
+
+    update: function()
+    {
+        var selectedNode = this.selectedTreeElement ? this.selectedTreeElement.representedObject : null;
+
+        this.removeChildren();
+
+        if (!this.rootDOMNode)
+            return;
+
+        var treeElement;
+        if (this._includeRootDOMNode) {
+            treeElement = new WebInspector.ElementsTreeElement(this.rootDOMNode);
+            treeElement.selectable = this._selectEnabled;
+            this.appendChild(treeElement);
+        } else {
+            // FIXME: this could use findTreeElement to reuse a tree element if it already exists
+            var node = this.rootDOMNode.firstChild;
+            while (node) {
+                treeElement = new WebInspector.ElementsTreeElement(node);
+                treeElement.selectable = this._selectEnabled;
+                this.appendChild(treeElement);
+                node = node.nextSibling;
+            }
+        }
+
+        if (selectedNode)
+            this._revealAndSelectNode(selectedNode, true);
+    },
+
+    updateSelection: function()
+    {
+        if (!this.selectedTreeElement)
+            return;
+        var element = this.treeOutline.selectedTreeElement;
+        element.updateSelection();
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     */
+    updateOpenCloseTags: function(node)
+    {
+        var treeElement = this.findTreeElement(node);
+        if (treeElement)
+            treeElement.updateTitle();
+        var children = treeElement.children;
+        var closingTagElement = children[children.length - 1];
+        if (closingTagElement && closingTagElement._elementCloseTag)
+            closingTagElement.updateTitle();
+    },
+
+    _selectedNodeChanged: function()
+    {
+        this._eventSupport.dispatchEventToListeners(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedDOMNode);
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     */
+    findTreeElement: function(node)
+    {
+        function isAncestorNode(ancestor, node)
+        {
+            return ancestor.isAncestor(node);
+        }
+
+        function parentNode(node)
+        {
+            return node.parentNode;
+        }
+
+        var treeElement = TreeOutline.prototype.findTreeElement.call(this, node, isAncestorNode, parentNode);
+        if (!treeElement && node.nodeType() === Node.TEXT_NODE) {
+            // The text node might have been inlined if it was short, so try to find the parent element.
+            treeElement = TreeOutline.prototype.findTreeElement.call(this, node.parentNode, isAncestorNode, parentNode);
+        }
+
+        return treeElement;
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     */
+    createTreeElementFor: function(node)
+    {
+        var treeElement = this.findTreeElement(node);
+        if (treeElement)
+            return treeElement;
+        if (!node.parentNode)
+            return null;
+
+        treeElement = this.createTreeElementFor(node.parentNode);
+        if (treeElement && treeElement.showChild(node.index))
+            return treeElement.children[node.index];
+
+        return null;
+    },
+
+    set suppressRevealAndSelect(x)
+    {
+        if (this._suppressRevealAndSelect === x)
+            return;
+        this._suppressRevealAndSelect = x;
+    },
+
+    _revealAndSelectNode: function(node, omitFocus)
+    {
+        if (!node || this._suppressRevealAndSelect)
+            return;
+
+        var treeElement = this.createTreeElementFor(node);
+        if (!treeElement)
+            return;
+
+        treeElement.revealAndSelect(omitFocus);
+    },
+
+    _treeElementFromEvent: function(event)
+    {
+        var scrollContainer = this.element.parentElement;
+
+        // We choose this X coordinate based on the knowledge that our list
+        // items extend at least to the right edge of the outer <ol> container.
+        // In the no-word-wrap mode the outer <ol> may be wider than the tree container
+        // (and partially hidden), in which case we are left to use only its right boundary.
+        var x = scrollContainer.totalOffsetLeft() + scrollContainer.offsetWidth - 36;
+
+        var y = event.pageY;
+
+        // Our list items have 1-pixel cracks between them vertically. We avoid
+        // the cracks by checking slightly above and slightly below the mouse
+        // and seeing if we hit the same element each time.
+        var elementUnderMouse = this.treeElementFromPoint(x, y);
+        var elementAboveMouse = this.treeElementFromPoint(x, y - 2);
+        var element;
+        if (elementUnderMouse === elementAboveMouse)
+            element = elementUnderMouse;
+        else
+            element = this.treeElementFromPoint(x, y + 2);
+
+        return element;
+    },
+
+    _onmousedown: function(event)
+    {
+        var element = this._treeElementFromEvent(event);
+
+        if (!element || element.isEventWithinDisclosureTriangle(event))
+            return;
+
+        element.select();
+    },
+
+    _onmousemove: function(event)
+    {
+        var element = this._treeElementFromEvent(event);
+        if (element && this._previousHoveredElement === element)
+            return;
+
+        if (this._previousHoveredElement) {
+            this._previousHoveredElement.hovered = false;
+            delete this._previousHoveredElement;
+        }
+
+        if (element) {
+            element.hovered = true;
+            this._previousHoveredElement = element;
+        }
+
+        WebInspector.domAgent.highlightDOMNode(element ? element.representedObject.id : 0);
+    },
+
+    _onmouseout: function(event)
+    {
+        var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
+        if (nodeUnderMouse && nodeUnderMouse.isDescendant(this.element))
+            return;
+
+        if (this._previousHoveredElement) {
+            this._previousHoveredElement.hovered = false;
+            delete this._previousHoveredElement;
+        }
+
+        WebInspector.domAgent.hideDOMNodeHighlight();
+    },
+
+    _ondragstart: function(event)
+    {
+        if (!window.getSelection().isCollapsed)
+            return false;
+        if (event.target.nodeName === "A")
+            return false;
+
+        var treeElement = this._treeElementFromEvent(event);
+        if (!treeElement)
+            return false;
+
+        if (!this._isValidDragSourceOrTarget(treeElement))
+            return false;
+
+        if (treeElement.representedObject.nodeName() === "BODY" || treeElement.representedObject.nodeName() === "HEAD")
+            return false;
+
+        event.dataTransfer.setData("text/plain", treeElement.listItemElement.textContent);
+        event.dataTransfer.effectAllowed = "copyMove";
+        this._treeElementBeingDragged = treeElement;
+
+        WebInspector.domAgent.hideDOMNodeHighlight();
+
+        return true;
+    },
+
+    _ondragover: function(event)
+    {
+        if (!this._treeElementBeingDragged)
+            return false;
+
+        var treeElement = this._treeElementFromEvent(event);
+        if (!this._isValidDragSourceOrTarget(treeElement))
+            return false;
+
+        var node = treeElement.representedObject;
+        while (node) {
+            if (node === this._treeElementBeingDragged.representedObject)
+                return false;
+            node = node.parentNode;
+        }
+
+        treeElement.updateSelection();
+        treeElement.listItemElement.addStyleClass("elements-drag-over");
+        this._dragOverTreeElement = treeElement;
+        event.preventDefault();
+        event.dataTransfer.dropEffect = 'move';
+        return false;
+    },
+
+    _ondragleave: function(event)
+    {
+        this._clearDragOverTreeElementMarker();
+        event.preventDefault();
+        return false;
+    },
+
+    _isValidDragSourceOrTarget: function(treeElement)
+    {
+        if (!treeElement)
+            return false;
+
+        var node = treeElement.representedObject;
+        if (!(node instanceof WebInspector.DOMNode))
+            return false;
+
+        if (!node.parentNode || node.parentNode.nodeType() !== Node.ELEMENT_NODE)
+            return false;
+
+        return true;
+    },
+
+    _ondrop: function(event)
+    {
+        event.preventDefault();
+        var treeElement = this._treeElementFromEvent(event);
+        if (treeElement)
+            this._doMove(treeElement);
+    },
+
+    _doMove: function(treeElement)
+    {
+        if (!this._treeElementBeingDragged)
+            return;
+
+        var parentNode;
+        var anchorNode;
+
+        if (treeElement._elementCloseTag) {
+            // Drop onto closing tag -> insert as last child.
+            parentNode = treeElement.representedObject;
+        } else {
+            var dragTargetNode = treeElement.representedObject;
+            parentNode = dragTargetNode.parentNode;
+            anchorNode = dragTargetNode;
+        }
+
+        var wasExpanded = this._treeElementBeingDragged.expanded;
+        this._treeElementBeingDragged.representedObject.moveTo(parentNode, anchorNode, this._selectNodeAfterEdit.bind(this, null, wasExpanded));
+
+        delete this._treeElementBeingDragged;
+    },
+
+    _ondragend: function(event)
+    {
+        event.preventDefault();
+        this._clearDragOverTreeElementMarker();
+        delete this._treeElementBeingDragged;
+    },
+
+    _clearDragOverTreeElementMarker: function()
+    {
+        if (this._dragOverTreeElement) {
+            this._dragOverTreeElement.updateSelection();
+            this._dragOverTreeElement.listItemElement.removeStyleClass("elements-drag-over");
+            delete this._dragOverTreeElement;
+        }
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onkeydown: function(event)
+    {
+        var keyboardEvent = /** @type {KeyboardEvent} */ (event);
+        var node = this.selectedDOMNode();
+        var treeElement = this.getCachedTreeElement(node);
+        if (!treeElement)
+            return;
+
+        if (!treeElement._editing && WebInspector.KeyboardShortcut.hasNoModifiers(keyboardEvent) && keyboardEvent.keyCode === WebInspector.KeyboardShortcut.Keys.H.code) {
+            this._toggleHideShortcut(node);
+            event.consume(true);
+            return;
+        }
+    },
+
+    _contextMenuEventFired: function(event)
+    {
+        if (!this._showInElementsPanelEnabled)
+            return;
+
+        var treeElement = this._treeElementFromEvent(event);
+        if (!treeElement)
+            return;
+
+        function focusElement()
+        {
+            // Force elements module load.
+            WebInspector.showPanel("elements");
+            WebInspector.domAgent.inspectElement(treeElement.representedObject.id);
+        }
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Elements panel" : "Reveal in Elements Panel"), focusElement.bind(this));
+        contextMenu.show();
+    },
+
+    populateContextMenu: function(contextMenu, event)
+    {
+        var treeElement = this._treeElementFromEvent(event);
+        if (!treeElement)
+            return;
+
+        var isTag = treeElement.representedObject.nodeType() === Node.ELEMENT_NODE;
+        var textNode = event.target.enclosingNodeOrSelfWithClass("webkit-html-text-node");
+        if (textNode && textNode.hasStyleClass("bogus"))
+            textNode = null;
+        var commentNode = event.target.enclosingNodeOrSelfWithClass("webkit-html-comment");
+        contextMenu.appendApplicableItems(event.target);
+        if (textNode) {
+            contextMenu.appendSeparator();
+            treeElement._populateTextContextMenu(contextMenu, textNode);
+        } else if (isTag) {
+            contextMenu.appendSeparator();
+            treeElement._populateTagContextMenu(contextMenu, event);
+        } else if (commentNode) {
+            contextMenu.appendSeparator();
+            treeElement._populateNodeContextMenu(contextMenu, textNode);
+        }
+    },
+
+    adjustCollapsedRange: function()
+    {
+    },
+
+    _updateModifiedNodes: function()
+    {
+        if (this._elementsTreeUpdater)
+            this._elementsTreeUpdater._updateModifiedNodes();
+    },
+
+    _populateContextMenu: function(contextMenu, node)
+    {
+        if (this._contextMenuCallback)
+            this._contextMenuCallback(contextMenu, node);
+    },
+
+    handleShortcut: function(event)
+    {
+        var node = this.selectedDOMNode();
+        var treeElement = this.getCachedTreeElement(node);
+        if (!node || !treeElement)
+            return;
+
+        if (event.keyIdentifier === "F2") {
+            this._toggleEditAsHTML(node);
+            event.handled = true;
+            return;
+        }
+
+        if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && node.parentNode) {
+            if (event.keyIdentifier === "Up" && node.previousSibling) {
+                node.moveTo(node.parentNode, node.previousSibling, this._selectNodeAfterEdit.bind(this, null, treeElement.expanded));
+                event.handled = true;
+                return;
+            }
+            if (event.keyIdentifier === "Down" && node.nextSibling) {
+                node.moveTo(node.parentNode, node.nextSibling.nextSibling, this._selectNodeAfterEdit.bind(this, null, treeElement.expanded));
+                event.handled = true;
+                return;
+            }
+        }
+    },
+
+    _toggleEditAsHTML: function(node)
+    {
+        var treeElement = this.getCachedTreeElement(node);
+        if (!treeElement)
+            return;
+
+        if (treeElement._editing && treeElement._htmlEditElement && WebInspector.isBeingEdited(treeElement._htmlEditElement))
+            treeElement._editing.commit();
+        else
+            treeElement._editAsHTML();
+    },
+
+    _selectNodeAfterEdit: function(fallbackNode, wasExpanded, error, nodeId)
+    {
+        if (error)
+            return;
+
+        // Select it and expand if necessary. We force tree update so that it processes dom events and is up to date.
+        this._updateModifiedNodes();
+
+        var newNode = WebInspector.domAgent.nodeForId(nodeId) || fallbackNode;
+        if (!newNode)
+            return;
+
+        this.selectDOMNode(newNode, true);
+
+        var newTreeItem = this.findTreeElement(newNode);
+        if (wasExpanded) {
+            if (newTreeItem)
+                newTreeItem.expand();
+        }
+        return newTreeItem;
+    },
+
+    /**
+     * Runs a script on the node's remote object that toggles a class name on
+     * the node and injects a stylesheet into the head of the node's document
+     * containing a rule to set "visibility: hidden" on the class and all it's
+     * ancestors.
+     *
+     * @param {WebInspector.DOMNode} node
+     * @param {function(?WebInspector.RemoteObject)=} userCallback
+     */
+    _toggleHideShortcut: function(node, userCallback)
+    {
+        function resolvedNode(object)
+        {
+            if (!object)
+                return;
+
+            function toggleClassAndInjectStyleRule()
+            {
+                const className = "__web-inspector-hide-shortcut__";
+                const styleTagId = "__web-inspector-hide-shortcut-style__";
+                const styleRule = ".__web-inspector-hide-shortcut__, .__web-inspector-hide-shortcut__ * { visibility: hidden !important; }";
+
+                this.classList.toggle(className);
+
+                var style = document.head.querySelector("style#" + styleTagId);
+                if (style)
+                    return;
+
+                style = document.createElement("style");
+                style.id = styleTagId;
+                style.type = "text/css";
+                style.innerHTML = styleRule;
+                document.head.appendChild(style);
+            }
+
+            object.callFunction(toggleClassAndInjectStyleRule, undefined, userCallback);
+            object.release();
+        }
+
+        WebInspector.RemoteObject.resolveNode(node, "", resolvedNode);
+    },
+
+    __proto__: TreeOutline.prototype
+}
+
+/**
+ * @interface
+ */
+WebInspector.ElementsTreeOutline.ElementDecorator = function()
+{
+}
+
+WebInspector.ElementsTreeOutline.ElementDecorator.prototype = {
+    /**
+     * @param {WebInspector.DOMNode} node
+     */
+    decorate: function(node)
+    {
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} node
+     */
+    decorateAncestor: function(node)
+    {
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ElementsTreeOutline.ElementDecorator}
+ */
+WebInspector.ElementsTreeOutline.PseudoStateDecorator = function()
+{
+    WebInspector.ElementsTreeOutline.ElementDecorator.call(this);
+}
+
+WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName = "pseudoState";
+
+WebInspector.ElementsTreeOutline.PseudoStateDecorator.prototype = {
+    decorate: function(node)
+    {
+        if (node.nodeType() !== Node.ELEMENT_NODE)
+            return null;
+        var propertyValue = node.getUserProperty(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
+        if (!propertyValue)
+            return null;
+        return WebInspector.UIString("Element state: %s", ":" + propertyValue.join(", :"));
+    },
+
+    decorateAncestor: function(node)
+    {
+        if (node.nodeType() !== Node.ELEMENT_NODE)
+            return null;
+
+        var descendantCount = node.descendantUserPropertyCount(WebInspector.ElementsTreeOutline.PseudoStateDecorator.PropertyName);
+        if (!descendantCount)
+            return null;
+        if (descendantCount === 1)
+            return WebInspector.UIString("%d descendant with forced state", descendantCount);
+        return WebInspector.UIString("%d descendants with forced state", descendantCount);
+    },
+
+    __proto__: WebInspector.ElementsTreeOutline.ElementDecorator.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {boolean=} elementCloseTag
+ */
+WebInspector.ElementsTreeElement = function(node, elementCloseTag)
+{
+    this._elementCloseTag = elementCloseTag;
+    var hasChildrenOverride = !elementCloseTag && node.hasChildNodes() && !this._showInlineText(node);
+
+    // The title will be updated in onattach.
+    TreeElement.call(this, "", node, hasChildrenOverride);
+
+    if (this.representedObject.nodeType() == Node.ELEMENT_NODE && !elementCloseTag)
+        this._canAddAttributes = true;
+    this._searchQuery = null;
+    this._expandedChildrenLimit = WebInspector.ElementsTreeElement.InitialChildrenLimit;
+}
+
+WebInspector.ElementsTreeElement.InitialChildrenLimit = 500;
+
+// A union of HTML4 and HTML5-Draft elements that explicitly
+// or implicitly (for HTML5) forbid the closing tag.
+// FIXME: Revise once HTML5 Final is published.
+WebInspector.ElementsTreeElement.ForbiddenClosingTagElements = [
+    "area", "base", "basefont", "br", "canvas", "col", "command", "embed", "frame",
+    "hr", "img", "input", "isindex", "keygen", "link", "meta", "param", "source"
+].keySet();
+
+// These tags we do not allow editing their tag name.
+WebInspector.ElementsTreeElement.EditTagBlacklist = [
+    "html", "head", "body"
+].keySet();
+
+WebInspector.ElementsTreeElement.prototype = {
+    highlightSearchResults: function(searchQuery)
+    {
+        if (this._searchQuery !== searchQuery) {
+            this._updateSearchHighlight(false);
+            delete this._highlightResult; // A new search query.
+        }
+
+        this._searchQuery = searchQuery;
+        this._searchHighlightsVisible = true;
+        this.updateTitle(true);
+    },
+
+    hideSearchHighlights: function()
+    {
+        delete this._searchHighlightsVisible;
+        this._updateSearchHighlight(false);
+    },
+
+    _updateSearchHighlight: function(show)
+    {
+        if (!this._highlightResult)
+            return;
+
+        function updateEntryShow(entry)
+        {
+            switch (entry.type) {
+                case "added":
+                    entry.parent.insertBefore(entry.node, entry.nextSibling);
+                    break;
+                case "changed":
+                    entry.node.textContent = entry.newText;
+                    break;
+            }
+        }
+
+        function updateEntryHide(entry)
+        {
+            switch (entry.type) {
+                case "added":
+                    if (entry.node.parentElement)
+                        entry.node.parentElement.removeChild(entry.node);
+                    break;
+                case "changed":
+                    entry.node.textContent = entry.oldText;
+                    break;
+            }
+        }
+
+        // Preserve the semantic of node by following the order of updates for hide and show.
+        if (show) {
+            for (var i = 0, size = this._highlightResult.length; i < size; ++i)
+                updateEntryShow(this._highlightResult[i]);
+        } else {
+            for (var i = (this._highlightResult.length - 1); i >= 0; --i)
+                updateEntryHide(this._highlightResult[i]);
+        }
+    },
+
+    get hovered()
+    {
+        return this._hovered;
+    },
+
+    set hovered(x)
+    {
+        if (this._hovered === x)
+            return;
+
+        this._hovered = x;
+
+        if (this.listItemElement) {
+            if (x) {
+                this.updateSelection();
+                this.listItemElement.addStyleClass("hovered");
+            } else {
+                this.listItemElement.removeStyleClass("hovered");
+            }
+        }
+    },
+
+    get expandedChildrenLimit()
+    {
+        return this._expandedChildrenLimit;
+    },
+
+    set expandedChildrenLimit(x)
+    {
+        if (this._expandedChildrenLimit === x)
+            return;
+
+        this._expandedChildrenLimit = x;
+        if (this.treeOutline && !this._updateChildrenInProgress)
+            this._updateChildren(true);
+    },
+
+    get expandedChildCount()
+    {
+        var count = this.children.length;
+        if (count && this.children[count - 1]._elementCloseTag)
+            count--;
+        if (count && this.children[count - 1].expandAllButton)
+            count--;
+        return count;
+    },
+
+    showChild: function(index)
+    {
+        if (this._elementCloseTag)
+            return;
+
+        if (index >= this.expandedChildrenLimit) {
+            this._expandedChildrenLimit = index + 1;
+            this._updateChildren(true);
+        }
+
+        // Whether index-th child is visible in the children tree
+        return this.expandedChildCount > index;
+    },
+
+    updateSelection: function()
+    {
+        var listItemElement = this.listItemElement;
+        if (!listItemElement)
+            return;
+
+        if (!this._readyToUpdateSelection) {
+            if (document.body.offsetWidth > 0)
+                this._readyToUpdateSelection = true;
+            else {
+                // The stylesheet hasn't loaded yet or the window is closed,
+                // so we can't calculate what we need. Return early.
+                return;
+            }
+        }
+
+        if (!this.selectionElement) {
+            this.selectionElement = document.createElement("div");
+            this.selectionElement.className = "selection selected";
+            listItemElement.insertBefore(this.selectionElement, listItemElement.firstChild);
+        }
+
+        this.selectionElement.style.height = listItemElement.offsetHeight + "px";
+    },
+
+    onattach: function()
+    {
+        if (this._hovered) {
+            this.updateSelection();
+            this.listItemElement.addStyleClass("hovered");
+        }
+
+        this.updateTitle();
+        this._preventFollowingLinksOnDoubleClick();
+        this.listItemElement.draggable = true;
+    },
+
+    _preventFollowingLinksOnDoubleClick: function()
+    {
+        var links = this.listItemElement.querySelectorAll("li > .webkit-html-tag > .webkit-html-attribute > .webkit-html-external-link, li > .webkit-html-tag > .webkit-html-attribute > .webkit-html-resource-link");
+        if (!links)
+            return;
+
+        for (var i = 0; i < links.length; ++i)
+            links[i].preventFollowOnDoubleClick = true;
+    },
+
+    onpopulate: function()
+    {
+        if (this.children.length || this._showInlineText(this.representedObject) || this._elementCloseTag)
+            return;
+
+        this.updateChildren();
+    },
+
+    /**
+     * @param {boolean=} fullRefresh
+     */
+    updateChildren: function(fullRefresh)
+    {
+        if (this._elementCloseTag)
+            return;
+        this.representedObject.getChildNodes(this._updateChildren.bind(this, fullRefresh));
+    },
+
+    /**
+     * @param {boolean=} closingTag
+     */
+    insertChildElement: function(child, index, closingTag)
+    {
+        var newElement = new WebInspector.ElementsTreeElement(child, closingTag);
+        newElement.selectable = this.treeOutline._selectEnabled;
+        this.insertChild(newElement, index);
+        return newElement;
+    },
+
+    moveChild: function(child, targetIndex)
+    {
+        var wasSelected = child.selected;
+        this.removeChild(child);
+        this.insertChild(child, targetIndex);
+        if (wasSelected)
+            child.select();
+    },
+
+    /**
+     * @param {boolean=} fullRefresh
+     */
+    _updateChildren: function(fullRefresh)
+    {
+        if (this._updateChildrenInProgress || !this.treeOutline._visible)
+            return;
+
+        this._updateChildrenInProgress = true;
+        var selectedNode = this.treeOutline.selectedDOMNode();
+        var originalScrollTop = 0;
+        if (fullRefresh) {
+            var treeOutlineContainerElement = this.treeOutline.element.parentNode;
+            originalScrollTop = treeOutlineContainerElement.scrollTop;
+            var selectedTreeElement = this.treeOutline.selectedTreeElement;
+            if (selectedTreeElement && selectedTreeElement.hasAncestor(this))
+                this.select();
+            this.removeChildren();
+        }
+
+        var treeElement = this;
+        var treeChildIndex = 0;
+        var elementToSelect;
+
+        function updateChildrenOfNode(node)
+        {
+            var treeOutline = treeElement.treeOutline;
+            var child = node.firstChild;
+            while (child) {
+                var currentTreeElement = treeElement.children[treeChildIndex];
+                if (!currentTreeElement || currentTreeElement.representedObject !== child) {
+                    // Find any existing element that is later in the children list.
+                    var existingTreeElement = null;
+                    for (var i = (treeChildIndex + 1), size = treeElement.expandedChildCount; i < size; ++i) {
+                        if (treeElement.children[i].representedObject === child) {
+                            existingTreeElement = treeElement.children[i];
+                            break;
+                        }
+                    }
+
+                    if (existingTreeElement && existingTreeElement.parent === treeElement) {
+                        // If an existing element was found and it has the same parent, just move it.
+                        treeElement.moveChild(existingTreeElement, treeChildIndex);
+                    } else {
+                        // No existing element found, insert a new element.
+                        if (treeChildIndex < treeElement.expandedChildrenLimit) {
+                            var newElement = treeElement.insertChildElement(child, treeChildIndex);
+                            if (child === selectedNode)
+                                elementToSelect = newElement;
+                            if (treeElement.expandedChildCount > treeElement.expandedChildrenLimit)
+                                treeElement.expandedChildrenLimit++;
+                        }
+                    }
+                }
+
+                child = child.nextSibling;
+                ++treeChildIndex;
+            }
+        }
+
+        // Remove any tree elements that no longer have this node (or this node's contentDocument) as their parent.
+        for (var i = (this.children.length - 1); i >= 0; --i) {
+            var currentChild = this.children[i];
+            var currentNode = currentChild.representedObject;
+            var currentParentNode = currentNode.parentNode;
+
+            if (currentParentNode === this.representedObject)
+                continue;
+
+            var selectedTreeElement = this.treeOutline.selectedTreeElement;
+            if (selectedTreeElement && (selectedTreeElement === currentChild || selectedTreeElement.hasAncestor(currentChild)))
+                this.select();
+
+            this.removeChildAtIndex(i);
+        }
+
+        updateChildrenOfNode(this.representedObject);
+        this.adjustCollapsedRange();
+
+        var lastChild = this.children[this.children.length - 1];
+        if (this.representedObject.nodeType() == Node.ELEMENT_NODE && (!lastChild || !lastChild._elementCloseTag))
+            this.insertChildElement(this.representedObject, this.children.length, true);
+
+        // We want to restore the original selection and tree scroll position after a full refresh, if possible.
+        if (fullRefresh && elementToSelect) {
+            elementToSelect.select();
+            if (treeOutlineContainerElement && originalScrollTop <= treeOutlineContainerElement.scrollHeight)
+                treeOutlineContainerElement.scrollTop = originalScrollTop;
+        }
+
+        delete this._updateChildrenInProgress;
+    },
+
+    adjustCollapsedRange: function()
+    {
+        // Ensure precondition: only the tree elements for node children are found in the tree
+        // (not the Expand All button or the closing tag).
+        if (this.expandAllButtonElement && this.expandAllButtonElement.__treeElement.parent)
+            this.removeChild(this.expandAllButtonElement.__treeElement);
+
+        const node = this.representedObject;
+        if (!node.children)
+            return;
+        const childNodeCount = node.children.length;
+
+        // In case some nodes from the expanded range were removed, pull some nodes from the collapsed range into the expanded range at the bottom.
+        for (var i = this.expandedChildCount, limit = Math.min(this.expandedChildrenLimit, childNodeCount); i < limit; ++i)
+            this.insertChildElement(node.children[i], i);
+
+        const expandedChildCount = this.expandedChildCount;
+        if (childNodeCount > this.expandedChildCount) {
+            var targetButtonIndex = expandedChildCount;
+            if (!this.expandAllButtonElement) {
+                var button = document.createElement("button");
+                button.className = "show-all-nodes";
+                button.value = "";
+                var item = new TreeElement(button, null, false);
+                item.selectable = false;
+                item.expandAllButton = true;
+                this.insertChild(item, targetButtonIndex);
+                this.expandAllButtonElement = item.listItemElement.firstChild;
+                this.expandAllButtonElement.__treeElement = item;
+                this.expandAllButtonElement.addEventListener("click", this.handleLoadAllChildren.bind(this), false);
+            } else if (!this.expandAllButtonElement.__treeElement.parent)
+                this.insertChild(this.expandAllButtonElement.__treeElement, targetButtonIndex);
+            this.expandAllButtonElement.textContent = WebInspector.UIString("Show All Nodes (%d More)", childNodeCount - expandedChildCount);
+        } else if (this.expandAllButtonElement)
+            delete this.expandAllButtonElement;
+    },
+
+    handleLoadAllChildren: function()
+    {
+        this.expandedChildrenLimit = Math.max(this.representedObject._childNodeCount, this.expandedChildrenLimit + WebInspector.ElementsTreeElement.InitialChildrenLimit);
+    },
+
+    expandRecursively: function()
+    {
+        function callback()
+        {
+            TreeElement.prototype.expandRecursively.call(this, Number.MAX_VALUE);
+        }
+        
+        this.representedObject.getSubtree(-1, callback.bind(this));
+    },
+
+    onexpand: function()
+    {
+        if (this._elementCloseTag)
+            return;
+
+        this.updateTitle();
+        this.treeOutline.updateSelection();
+    },
+
+    oncollapse: function()
+    {
+        if (this._elementCloseTag)
+            return;
+
+        this.updateTitle();
+        this.treeOutline.updateSelection();
+    },
+
+    onreveal: function()
+    {
+        if (this.listItemElement) {
+            var tagSpans = this.listItemElement.getElementsByClassName("webkit-html-tag-name");
+            if (tagSpans.length)
+                tagSpans[0].scrollIntoViewIfNeeded(false);
+            else
+                this.listItemElement.scrollIntoViewIfNeeded(false);
+        }
+    },
+
+    onselect: function(selectedByUser)
+    {
+        this.treeOutline.suppressRevealAndSelect = true;
+        this.treeOutline.selectDOMNode(this.representedObject, selectedByUser);
+        if (selectedByUser)
+            WebInspector.domAgent.highlightDOMNode(this.representedObject.id);
+        this.updateSelection();
+        this.treeOutline.suppressRevealAndSelect = false;
+        return true;
+    },
+
+    ondelete: function()
+    {
+        var startTagTreeElement = this.treeOutline.findTreeElement(this.representedObject);
+        startTagTreeElement ? startTagTreeElement.remove() : this.remove();
+        return true;
+    },
+
+    onenter: function()
+    {
+        // On Enter or Return start editing the first attribute
+        // or create a new attribute on the selected element.
+        if (this._editing)
+            return false;
+
+        this._startEditing();
+
+        // prevent a newline from being immediately inserted
+        return true;
+    },
+
+    selectOnMouseDown: function(event)
+    {
+        TreeElement.prototype.selectOnMouseDown.call(this, event);
+
+        if (this._editing)
+            return;
+
+        if (this.treeOutline._showInElementsPanelEnabled) {
+            WebInspector.showPanel("elements");
+            this.treeOutline.selectDOMNode(this.representedObject, true);
+        }
+
+        // Prevent selecting the nearest word on double click.
+        if (event.detail >= 2)
+            event.preventDefault();
+    },
+
+    ondblclick: function(event)
+    {
+        if (this._editing || this._elementCloseTag)
+            return;
+
+        if (this._startEditingTarget(event.target))
+            return;
+
+        if (this.hasChildren && !this.expanded)
+            this.expand();
+    },
+
+    _insertInLastAttributePosition: function(tag, node)
+    {
+        if (tag.getElementsByClassName("webkit-html-attribute").length > 0)
+            tag.insertBefore(node, tag.lastChild);
+        else {
+            var nodeName = tag.textContent.match(/^<(.*?)>$/)[1];
+            tag.textContent = '';
+            tag.appendChild(document.createTextNode('<'+nodeName));
+            tag.appendChild(node);
+            tag.appendChild(document.createTextNode('>'));
+        }
+
+        this.updateSelection();
+    },
+
+    _startEditingTarget: function(eventTarget)
+    {
+        if (this.treeOutline.selectedDOMNode() != this.representedObject)
+            return;
+
+        if (this.representedObject.nodeType() != Node.ELEMENT_NODE && this.representedObject.nodeType() != Node.TEXT_NODE)
+            return false;
+
+        var textNode = eventTarget.enclosingNodeOrSelfWithClass("webkit-html-text-node");
+        if (textNode)
+            return this._startEditingTextNode(textNode);
+
+        var attribute = eventTarget.enclosingNodeOrSelfWithClass("webkit-html-attribute");
+        if (attribute)
+            return this._startEditingAttribute(attribute, eventTarget);
+
+        var tagName = eventTarget.enclosingNodeOrSelfWithClass("webkit-html-tag-name");
+        if (tagName)
+            return this._startEditingTagName(tagName);
+
+        var newAttribute = eventTarget.enclosingNodeOrSelfWithClass("add-attribute");
+        if (newAttribute)
+            return this._addNewAttribute();
+
+        return false;
+    },
+
+    _populateTagContextMenu: function(contextMenu, event)
+    {
+        var attribute = event.target.enclosingNodeOrSelfWithClass("webkit-html-attribute");
+        var newAttribute = event.target.enclosingNodeOrSelfWithClass("add-attribute");
+
+        // Add attribute-related actions.
+        var treeElement = this._elementCloseTag ? this.treeOutline.findTreeElement(this.representedObject) : this;
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add attribute" : "Add Attribute"), this._addNewAttribute.bind(treeElement));
+        if (attribute && !newAttribute)
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Edit attribute" : "Edit Attribute"), this._startEditingAttribute.bind(this, attribute, event.target));
+        contextMenu.appendSeparator();
+        if (this.treeOutline._setPseudoClassCallback) {
+            var pseudoSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Force element state" : "Force Element State"));
+            this._populateForcedPseudoStateItems(pseudoSubMenu);
+            contextMenu.appendSeparator();
+        }
+
+        this._populateNodeContextMenu(contextMenu);
+        this.treeOutline._populateContextMenu(contextMenu, this.representedObject);
+
+        contextMenu.appendSeparator();
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Scroll into view" : "Scroll into View"), this._scrollIntoView.bind(this)); 
+    },
+
+    _populateForcedPseudoStateItems: function(subMenu)
+    {
+        const pseudoClasses = ["active", "hover", "focus", "visited"];
+        var node = this.representedObject;
+        var forcedPseudoState = (node ? node.getUserProperty("pseudoState") : null) || [];
+        for (var i = 0; i < pseudoClasses.length; ++i) {
+            var pseudoClassForced = forcedPseudoState.indexOf(pseudoClasses[i]) >= 0;
+            subMenu.appendCheckboxItem(":" + pseudoClasses[i], this.treeOutline._setPseudoClassCallback.bind(null, node.id, pseudoClasses[i], !pseudoClassForced), pseudoClassForced, false);
+        }
+    },
+
+    _populateTextContextMenu: function(contextMenu, textNode)
+    {
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Edit text" : "Edit Text"), this._startEditingTextNode.bind(this, textNode));
+        this._populateNodeContextMenu(contextMenu);
+    },
+
+    _populateNodeContextMenu: function(contextMenu)
+    {
+        // Add free-form node-related actions.
+        contextMenu.appendItem(WebInspector.UIString("Edit as HTML"), this._editAsHTML.bind(this));
+        contextMenu.appendItem(WebInspector.UIString("Copy as HTML"), this._copyHTML.bind(this));
+        contextMenu.appendItem(WebInspector.UIString("Copy XPath"), this._copyXPath.bind(this));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Delete node" : "Delete Node"), this.remove.bind(this));
+    },
+
+    _startEditing: function()
+    {
+        if (this.treeOutline.selectedDOMNode() !== this.representedObject)
+            return;
+
+        var listItem = this._listItemNode;
+
+        if (this._canAddAttributes) {
+            var attribute = listItem.getElementsByClassName("webkit-html-attribute")[0];
+            if (attribute)
+                return this._startEditingAttribute(attribute, attribute.getElementsByClassName("webkit-html-attribute-value")[0]);
+
+            return this._addNewAttribute();
+        }
+
+        if (this.representedObject.nodeType() === Node.TEXT_NODE) {
+            var textNode = listItem.getElementsByClassName("webkit-html-text-node")[0];
+            if (textNode)
+                return this._startEditingTextNode(textNode);
+            return;
+        }
+    },
+
+    _addNewAttribute: function()
+    {
+        // Cannot just convert the textual html into an element without
+        // a parent node. Use a temporary span container for the HTML.
+        var container = document.createElement("span");
+        this._buildAttributeDOM(container, " ", "");
+        var attr = container.firstChild;
+        attr.style.marginLeft = "2px"; // overrides the .editing margin rule
+        attr.style.marginRight = "2px"; // overrides the .editing margin rule
+
+        var tag = this.listItemElement.getElementsByClassName("webkit-html-tag")[0];
+        this._insertInLastAttributePosition(tag, attr);
+        attr.scrollIntoViewIfNeeded(true);
+        return this._startEditingAttribute(attr, attr);
+    },
+
+    _triggerEditAttribute: function(attributeName)
+    {
+        var attributeElements = this.listItemElement.getElementsByClassName("webkit-html-attribute-name");
+        for (var i = 0, len = attributeElements.length; i < len; ++i) {
+            if (attributeElements[i].textContent === attributeName) {
+                for (var elem = attributeElements[i].nextSibling; elem; elem = elem.nextSibling) {
+                    if (elem.nodeType !== Node.ELEMENT_NODE)
+                        continue;
+
+                    if (elem.hasStyleClass("webkit-html-attribute-value"))
+                        return this._startEditingAttribute(elem.parentNode, elem);
+                }
+            }
+        }
+    },
+
+    _startEditingAttribute: function(attribute, elementForSelection)
+    {
+        if (WebInspector.isBeingEdited(attribute))
+            return true;
+
+        var attributeNameElement = attribute.getElementsByClassName("webkit-html-attribute-name")[0];
+        if (!attributeNameElement)
+            return false;
+
+        var attributeName = attributeNameElement.textContent;
+
+        function removeZeroWidthSpaceRecursive(node)
+        {
+            if (node.nodeType === Node.TEXT_NODE) {
+                node.nodeValue = node.nodeValue.replace(/\u200B/g, "");
+                return;
+            }
+
+            if (node.nodeType !== Node.ELEMENT_NODE)
+                return;
+
+            for (var child = node.firstChild; child; child = child.nextSibling)
+                removeZeroWidthSpaceRecursive(child);
+        }
+
+        // Remove zero-width spaces that were added by nodeTitleInfo.
+        removeZeroWidthSpaceRecursive(attribute);
+
+        var config = new WebInspector.EditingConfig(this._attributeEditingCommitted.bind(this), this._editingCancelled.bind(this), attributeName);
+        
+        function handleKeyDownEvents(event)
+        {
+            var isMetaOrCtrl = WebInspector.isMac() ?
+                event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey :
+                event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
+            if (isEnterKey(event) && (event.isMetaOrCtrlForTest || !config.multiline || isMetaOrCtrl))
+                return "commit";
+            else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
+                return "cancel";
+            else if (event.keyIdentifier === "U+0009") // Tab key
+                return "move-" + (event.shiftKey ? "backward" : "forward");
+            else {
+                WebInspector.handleElementValueModifications(event, attribute);
+                return "";
+            }
+        }
+
+        config.customFinishHandler = handleKeyDownEvents.bind(this);
+
+        this._editing = WebInspector.startEditing(attribute, config);
+
+        window.getSelection().setBaseAndExtent(elementForSelection, 0, elementForSelection, 1);
+
+        return true;
+    },
+
+    /**
+     * @param {Element} textNodeElement
+     */
+    _startEditingTextNode: function(textNodeElement)
+    {
+        if (WebInspector.isBeingEdited(textNodeElement))
+            return true;
+
+        var textNode = this.representedObject;
+        // We only show text nodes inline in elements if the element only
+        // has a single child, and that child is a text node.
+        if (textNode.nodeType() === Node.ELEMENT_NODE && textNode.firstChild)
+            textNode = textNode.firstChild;
+
+        var container = textNodeElement.enclosingNodeOrSelfWithClass("webkit-html-text-node");
+        if (container)
+            container.textContent = textNode.nodeValue(); // Strip the CSS or JS highlighting if present.
+        var config = new WebInspector.EditingConfig(this._textNodeEditingCommitted.bind(this, textNode), this._editingCancelled.bind(this));
+        this._editing = WebInspector.startEditing(textNodeElement, config);
+        window.getSelection().setBaseAndExtent(textNodeElement, 0, textNodeElement, 1);
+
+        return true;
+    },
+
+    /**
+     * @param {Element=} tagNameElement
+     */
+    _startEditingTagName: function(tagNameElement)
+    {
+        if (!tagNameElement) {
+            tagNameElement = this.listItemElement.getElementsByClassName("webkit-html-tag-name")[0];
+            if (!tagNameElement)
+                return false;
+        }
+
+        var tagName = tagNameElement.textContent;
+        if (WebInspector.ElementsTreeElement.EditTagBlacklist[tagName.toLowerCase()])
+            return false;
+
+        if (WebInspector.isBeingEdited(tagNameElement))
+            return true;
+
+        var closingTagElement = this._distinctClosingTagElement();
+
+        function keyupListener(event)
+        {
+            if (closingTagElement)
+                closingTagElement.textContent = "</" + tagNameElement.textContent + ">";
+        }
+
+        function editingComitted(element, newTagName)
+        {
+            tagNameElement.removeEventListener('keyup', keyupListener, false);
+            this._tagNameEditingCommitted.apply(this, arguments);
+        }
+
+        function editingCancelled()
+        {
+            tagNameElement.removeEventListener('keyup', keyupListener, false);
+            this._editingCancelled.apply(this, arguments);
+        }
+
+        tagNameElement.addEventListener('keyup', keyupListener, false);
+
+        var config = new WebInspector.EditingConfig(editingComitted.bind(this), editingCancelled.bind(this), tagName);
+        this._editing = WebInspector.startEditing(tagNameElement, config);
+        window.getSelection().setBaseAndExtent(tagNameElement, 0, tagNameElement, 1);
+        return true;
+    },
+
+    _startEditingAsHTML: function(commitCallback, error, initialValue)
+    {
+        if (error)
+            return;
+        if (this._editing)
+            return;
+
+        function consume(event)
+        {
+            if (event.eventPhase === Event.AT_TARGET)
+                event.consume(true);
+        }
+
+        initialValue = this._convertWhitespaceToEntities(initialValue);
+
+        this._htmlEditElement = document.createElement("div");
+        this._htmlEditElement.className = "source-code elements-tree-editor";
+
+        // Hide header items.
+        var child = this.listItemElement.firstChild;
+        while (child) {
+            child.style.display = "none";
+            child = child.nextSibling;
+        }
+        // Hide children item.
+        if (this._childrenListNode)
+            this._childrenListNode.style.display = "none";
+        // Append editor.
+        this.listItemElement.appendChild(this._htmlEditElement);
+        this.treeOutline.childrenListElement.parentElement.addEventListener("mousedown", consume, false);
+
+        this.updateSelection();
+
+        /**
+         * @param {Element} element
+         * @param {string} newValue
+         */
+        function commit(element, newValue)
+        {
+            commitCallback(initialValue, newValue);
+            dispose.call(this);
+        }
+
+        function dispose()
+        {
+            delete this._editing;
+
+            // Remove editor.
+            this.listItemElement.removeChild(this._htmlEditElement);
+            delete this._htmlEditElement;
+            // Unhide children item.
+            if (this._childrenListNode)
+                this._childrenListNode.style.removeProperty("display");
+            // Unhide header items.
+            var child = this.listItemElement.firstChild;
+            while (child) {
+                child.style.removeProperty("display");
+                child = child.nextSibling;
+            }
+
+            this.treeOutline.childrenListElement.parentElement.removeEventListener("mousedown", consume, false);
+            this.updateSelection();
+        }
+
+        var config = new WebInspector.EditingConfig(commit.bind(this), dispose.bind(this));
+        config.setMultilineOptions(initialValue, { name: "xml", htmlMode: true }, "web-inspector-html", true, true);
+        this._editing = WebInspector.startEditing(this._htmlEditElement, config);
+    },
+
+    _attributeEditingCommitted: function(element, newText, oldText, attributeName, moveDirection)
+    {
+        delete this._editing;
+
+        var treeOutline = this.treeOutline;
+        /**
+         * @param {Protocol.Error=} error
+         */
+        function moveToNextAttributeIfNeeded(error)
+        {
+            if (error)
+                this._editingCancelled(element, attributeName);
+
+            if (!moveDirection)
+                return;
+
+            treeOutline._updateModifiedNodes();
+
+            // Search for the attribute's position, and then decide where to move to.
+            var attributes = this.representedObject.attributes();
+            for (var i = 0; i < attributes.length; ++i) {
+                if (attributes[i].name !== attributeName)
+                    continue;
+
+                if (moveDirection === "backward") {
+                    if (i === 0)
+                        this._startEditingTagName();
+                    else
+                        this._triggerEditAttribute(attributes[i - 1].name);
+                } else {
+                    if (i === attributes.length - 1)
+                        this._addNewAttribute();
+                    else
+                        this._triggerEditAttribute(attributes[i + 1].name);
+                }
+                return;
+            }
+
+            // Moving From the "New Attribute" position.
+            if (moveDirection === "backward") {
+                if (newText === " ") {
+                    // Moving from "New Attribute" that was not edited
+                    if (attributes.length > 0)
+                        this._triggerEditAttribute(attributes[attributes.length - 1].name);
+                } else {
+                    // Moving from "New Attribute" that holds new value
+                    if (attributes.length > 1)
+                        this._triggerEditAttribute(attributes[attributes.length - 2].name);
+                }
+            } else if (moveDirection === "forward") {
+                if (!/^\s*$/.test(newText))
+                    this._addNewAttribute();
+                else
+                    this._startEditingTagName();
+            }
+        }
+
+        if (!attributeName.trim() && !newText.trim()) {
+            element.removeSelf();
+            moveToNextAttributeIfNeeded.call(this);
+            return;
+        }
+
+        if (oldText !== newText) {
+            this.representedObject.setAttribute(attributeName, newText, moveToNextAttributeIfNeeded.bind(this));
+            return;
+        }
+
+        moveToNextAttributeIfNeeded.call(this);
+    },
+
+    _tagNameEditingCommitted: function(element, newText, oldText, tagName, moveDirection)
+    {
+        delete this._editing;
+        var self = this;
+
+        function cancel()
+        {
+            var closingTagElement = self._distinctClosingTagElement();
+            if (closingTagElement)
+                closingTagElement.textContent = "</" + tagName + ">";
+
+            self._editingCancelled(element, tagName);
+            moveToNextAttributeIfNeeded.call(self);
+        }
+
+        function moveToNextAttributeIfNeeded()
+        {
+            if (moveDirection !== "forward") {
+                this._addNewAttribute();
+                return;
+            }
+
+            var attributes = this.representedObject.attributes();
+            if (attributes.length > 0)
+                this._triggerEditAttribute(attributes[0].name);
+            else
+                this._addNewAttribute();
+        }
+
+        newText = newText.trim();
+        if (newText === oldText) {
+            cancel();
+            return;
+        }
+
+        var treeOutline = this.treeOutline;
+        var wasExpanded = this.expanded;
+
+        function changeTagNameCallback(error, nodeId)
+        {
+            if (error || !nodeId) {
+                cancel();
+                return;
+            }
+            var newTreeItem = treeOutline._selectNodeAfterEdit(null, wasExpanded, error, nodeId);
+            moveToNextAttributeIfNeeded.call(newTreeItem);
+        }
+
+        this.representedObject.setNodeName(newText, changeTagNameCallback);
+    },
+
+    /**
+     * @param {WebInspector.DOMNode} textNode
+     * @param {Element} element
+     * @param {string} newText
+     */
+    _textNodeEditingCommitted: function(textNode, element, newText)
+    {
+        delete this._editing;
+
+        function callback()
+        {
+            this.updateTitle();
+        }
+        textNode.setNodeValue(newText, callback.bind(this));
+    },
+
+    /**
+     * @param {Element} element
+     * @param {*} context
+     */
+    _editingCancelled: function(element, context)
+    {
+        delete this._editing;
+
+        // Need to restore attributes structure.
+        this.updateTitle();
+    },
+
+    _distinctClosingTagElement: function()
+    {
+        // FIXME: Improve the Tree Element / Outline Abstraction to prevent crawling the DOM
+
+        // For an expanded element, it will be the last element with class "close"
+        // in the child element list.
+        if (this.expanded) {
+            var closers = this._childrenListNode.querySelectorAll(".close");
+            return closers[closers.length-1];
+        }
+
+        // Remaining cases are single line non-expanded elements with a closing
+        // tag, or HTML elements without a closing tag (such as <br>). Return
+        // null in the case where there isn't a closing tag.
+        var tags = this.listItemElement.getElementsByClassName("webkit-html-tag");
+        return (tags.length === 1 ? null : tags[tags.length-1]);
+    },
+
+    /**
+     * @param {boolean=} onlySearchQueryChanged
+     */
+    updateTitle: function(onlySearchQueryChanged)
+    {
+        // If we are editing, return early to prevent canceling the edit.
+        // After editing is committed updateTitle will be called.
+        if (this._editing)
+            return;
+
+        if (onlySearchQueryChanged) {
+            if (this._highlightResult)
+                this._updateSearchHighlight(false);
+        } else {
+            var highlightElement = document.createElement("span");
+            highlightElement.className = "highlight";
+            highlightElement.appendChild(this._nodeTitleInfo(WebInspector.linkifyURLAsNode).titleDOM);
+            this.title = highlightElement;
+            this._updateDecorations();
+            delete this._highlightResult;
+        }
+
+        delete this.selectionElement;
+        if (this.selected)
+            this.updateSelection();
+        this._preventFollowingLinksOnDoubleClick();
+        this._highlightSearchResults();
+    },
+
+    _createDecoratorElement: function()
+    {
+        var node = this.representedObject;
+        var decoratorMessages = [];
+        var parentDecoratorMessages = [];
+        for (var i = 0; i < this.treeOutline._nodeDecorators.length; ++i) {
+            var decorator = this.treeOutline._nodeDecorators[i];
+            var message = decorator.decorate(node);
+            if (message) {
+                decoratorMessages.push(message);
+                continue;
+            }
+
+            if (this.expanded || this._elementCloseTag)
+                continue;
+
+            message = decorator.decorateAncestor(node);
+            if (message)
+                parentDecoratorMessages.push(message)
+        }
+        if (!decoratorMessages.length && !parentDecoratorMessages.length)
+            return null;
+
+        var decoratorElement = document.createElement("div");
+        decoratorElement.addStyleClass("elements-gutter-decoration");
+        if (!decoratorMessages.length)
+            decoratorElement.addStyleClass("elements-has-decorated-children");
+        decoratorElement.title = decoratorMessages.concat(parentDecoratorMessages).join("\n");
+        return decoratorElement;
+    },
+
+    _updateDecorations: function()
+    {
+        if (this._decoratorElement && this._decoratorElement.parentElement)
+            this._decoratorElement.parentElement.removeChild(this._decoratorElement);
+        this._decoratorElement = this._createDecoratorElement();
+        if (this._decoratorElement && this.listItemElement)
+            this.listItemElement.insertBefore(this._decoratorElement, this.listItemElement.firstChild);
+    },
+
+    /**
+     * @param {WebInspector.DOMNode=} node
+     * @param {function(string, string, string, boolean=, string=)=} linkify
+     */
+    _buildAttributeDOM: function(parentElement, name, value, node, linkify)
+    {
+        var hasText = (value.length > 0);
+        var attrSpanElement = parentElement.createChild("span", "webkit-html-attribute");
+        var attrNameElement = attrSpanElement.createChild("span", "webkit-html-attribute-name");
+        attrNameElement.textContent = name;
+
+        if (hasText)
+            attrSpanElement.appendChild(document.createTextNode("=\u200B\""));
+
+        if (linkify && (name === "src" || name === "href")) {
+            var rewrittenHref = node.resolveURL(value);
+            value = value.replace(/([\/;:\)\]\}])/g, "$1\u200B");
+            if (rewrittenHref === null) {
+                var attrValueElement = attrSpanElement.createChild("span", "webkit-html-attribute-value");
+                attrValueElement.textContent = value;
+            } else {
+                if (value.startsWith("data:"))
+                    value = value.trimMiddle(60);
+                attrSpanElement.appendChild(linkify(rewrittenHref, value, "webkit-html-attribute-value", node.nodeName().toLowerCase() === "a"));
+            }
+        } else {
+            value = value.replace(/([\/;:\)\]\}])/g, "$1\u200B");
+            var attrValueElement = attrSpanElement.createChild("span", "webkit-html-attribute-value");
+            attrValueElement.textContent = value;
+        }
+
+        if (hasText)
+            attrSpanElement.appendChild(document.createTextNode("\""));
+    },
+
+    /**
+     * @param {function(string, string, string, boolean=, string=)=} linkify
+     */
+    _buildTagDOM: function(parentElement, tagName, isClosingTag, isDistinctTreeElement, linkify)
+    {
+        var node = /** @type WebInspector.DOMNode */ (this.representedObject);
+        var classes = [ "webkit-html-tag" ];
+        if (isClosingTag && isDistinctTreeElement)
+            classes.push("close");
+        if (node.isInShadowTree())
+            classes.push("shadow");
+        var tagElement = parentElement.createChild("span", classes.join(" "));
+        tagElement.appendChild(document.createTextNode("<"));
+        var tagNameElement = tagElement.createChild("span", isClosingTag ? "" : "webkit-html-tag-name");
+        tagNameElement.textContent = (isClosingTag ? "/" : "") + tagName;
+        if (!isClosingTag && node.hasAttributes()) {
+            var attributes = node.attributes();
+            for (var i = 0; i < attributes.length; ++i) {
+                var attr = attributes[i];
+                tagElement.appendChild(document.createTextNode(" "));
+                this._buildAttributeDOM(tagElement, attr.name, attr.value, node, linkify);
+            }
+        }
+        tagElement.appendChild(document.createTextNode(">"));
+        parentElement.appendChild(document.createTextNode("\u200B"));
+    },
+
+    _convertWhitespaceToEntities: function(text)
+    {
+        var result = "";
+        var lastIndexAfterEntity = 0;
+        var charToEntity = WebInspector.ElementsTreeOutline.MappedCharToEntity;
+        for (var i = 0, size = text.length; i < size; ++i) {
+            var char = text.charAt(i);
+            if (charToEntity[char]) {
+                result += text.substring(lastIndexAfterEntity, i) + "&" + charToEntity[char] + ";";
+                lastIndexAfterEntity = i + 1;
+            }
+        }
+        if (result) {
+            result += text.substring(lastIndexAfterEntity);
+            return result;
+        }
+        return text;
+    },
+
+    _nodeTitleInfo: function(linkify)
+    {
+        var node = this.representedObject;
+        var info = {titleDOM: document.createDocumentFragment(), hasChildren: this.hasChildren};
+
+        switch (node.nodeType()) {
+            case Node.ATTRIBUTE_NODE:
+                var value = node.value || "\u200B"; // Zero width space to force showing an empty value.
+                this._buildAttributeDOM(info.titleDOM, node.name, value);
+                break;
+
+            case Node.ELEMENT_NODE:
+                var tagName = node.nodeNameInCorrectCase();
+                if (this._elementCloseTag) {
+                    this._buildTagDOM(info.titleDOM, tagName, true, true);
+                    info.hasChildren = false;
+                    break;
+                }
+
+                this._buildTagDOM(info.titleDOM, tagName, false, false, linkify);
+
+                var textChild = this._singleTextChild(node);
+                var showInlineText = textChild && textChild.nodeValue().length < Preferences.maxInlineTextChildLength && !this.hasChildren;
+
+                if (!this.expanded && (!showInlineText && (this.treeOutline.isXMLMimeType || !WebInspector.ElementsTreeElement.ForbiddenClosingTagElements[tagName]))) {
+                    if (this.hasChildren) {
+                        var textNodeElement = info.titleDOM.createChild("span", "webkit-html-text-node bogus");
+                        textNodeElement.textContent = "\u2026";
+                        info.titleDOM.appendChild(document.createTextNode("\u200B"));
+                    }
+                    this._buildTagDOM(info.titleDOM, tagName, true, false);
+                }
+
+                // If this element only has a single child that is a text node,
+                // just show that text and the closing tag inline rather than
+                // create a subtree for them
+                if (showInlineText) {
+                    var textNodeElement = info.titleDOM.createChild("span", "webkit-html-text-node");
+                    textNodeElement.textContent = this._convertWhitespaceToEntities(textChild.nodeValue());
+                    info.titleDOM.appendChild(document.createTextNode("\u200B"));
+                    this._buildTagDOM(info.titleDOM, tagName, true, false);
+                    info.hasChildren = false;
+                }
+                break;
+
+            case Node.TEXT_NODE:
+                if (node.parentNode && node.parentNode.nodeName().toLowerCase() === "script") {
+                    var newNode = info.titleDOM.createChild("span", "webkit-html-text-node webkit-html-js-node");
+                    newNode.textContent = node.nodeValue();
+
+                    var javascriptSyntaxHighlighter = new WebInspector.DOMSyntaxHighlighter("text/javascript", true);
+                    javascriptSyntaxHighlighter.syntaxHighlightNode(newNode);
+                } else if (node.parentNode && node.parentNode.nodeName().toLowerCase() === "style") {
+                    var newNode = info.titleDOM.createChild("span", "webkit-html-text-node webkit-html-css-node");
+                    newNode.textContent = node.nodeValue();
+
+                    var cssSyntaxHighlighter = new WebInspector.DOMSyntaxHighlighter("text/css", true);
+                    cssSyntaxHighlighter.syntaxHighlightNode(newNode);
+                } else {
+                    info.titleDOM.appendChild(document.createTextNode("\""));
+                    var textNodeElement = info.titleDOM.createChild("span", "webkit-html-text-node");
+                    textNodeElement.textContent = this._convertWhitespaceToEntities(node.nodeValue());
+                    info.titleDOM.appendChild(document.createTextNode("\""));
+                }
+                break;
+
+            case Node.COMMENT_NODE:
+                var commentElement = info.titleDOM.createChild("span", "webkit-html-comment");
+                commentElement.appendChild(document.createTextNode("<!--" + node.nodeValue() + "-->"));
+                break;
+
+            case Node.DOCUMENT_TYPE_NODE:
+                var docTypeElement = info.titleDOM.createChild("span", "webkit-html-doctype");
+                docTypeElement.appendChild(document.createTextNode("<!DOCTYPE " + node.nodeName()));
+                if (node.publicId) {
+                    docTypeElement.appendChild(document.createTextNode(" PUBLIC \"" + node.publicId + "\""));
+                    if (node.systemId)
+                        docTypeElement.appendChild(document.createTextNode(" \"" + node.systemId + "\""));
+                } else if (node.systemId)
+                    docTypeElement.appendChild(document.createTextNode(" SYSTEM \"" + node.systemId + "\""));
+
+                if (node.internalSubset)
+                    docTypeElement.appendChild(document.createTextNode(" [" + node.internalSubset + "]"));
+
+                docTypeElement.appendChild(document.createTextNode(">"));
+                break;
+
+            case Node.CDATA_SECTION_NODE:
+                var cdataElement = info.titleDOM.createChild("span", "webkit-html-text-node");
+                cdataElement.appendChild(document.createTextNode("<![CDATA[" + node.nodeValue() + "]]>"));
+                break;
+            case Node.DOCUMENT_FRAGMENT_NODE:
+                var fragmentElement = info.titleDOM.createChild("span", "webkit-html-fragment");
+                fragmentElement.textContent = node.nodeNameInCorrectCase().collapseWhitespace();
+                if (node.isInShadowTree())
+                    fragmentElement.addStyleClass("shadow");
+                break;
+            default:
+                info.titleDOM.appendChild(document.createTextNode(node.nodeNameInCorrectCase().collapseWhitespace()));
+        }
+        return info;
+    },
+
+    _singleTextChild: function(node)
+    {
+        if (!node)
+            return null;
+
+        var firstChild = node.firstChild;
+        if (!firstChild || firstChild.nodeType() !== Node.TEXT_NODE)
+            return null;
+
+        if (node.hasShadowRoots())
+            return null;
+
+        var sibling = firstChild.nextSibling;
+        return sibling ? null : firstChild;
+    },
+
+    _showInlineText: function(node)
+    {
+        if (node.nodeType() === Node.ELEMENT_NODE) {
+            var textChild = this._singleTextChild(node);
+            if (textChild && textChild.nodeValue().length < Preferences.maxInlineTextChildLength)
+                return true;
+        }
+        return false;
+    },
+
+    remove: function()
+    {
+        var parentElement = this.parent;
+        if (!parentElement)
+            return;
+
+        var self = this;
+        function removeNodeCallback(error, removedNodeId)
+        {
+            if (error)
+                return;
+
+            parentElement.removeChild(self);
+            parentElement.adjustCollapsedRange();
+        }
+
+        if (!this.representedObject.parentNode || this.representedObject.parentNode.nodeType() === Node.DOCUMENT_NODE)
+            return;
+        this.representedObject.removeNode(removeNodeCallback);
+    },
+
+    _editAsHTML: function()
+    {
+        var treeOutline = this.treeOutline;
+        var node = this.representedObject;
+        var parentNode = node.parentNode;
+        var index = node.index;
+        var wasExpanded = this.expanded;
+
+        function selectNode(error, nodeId)
+        {
+            if (error)
+                return;
+
+            // Select it and expand if necessary. We force tree update so that it processes dom events and is up to date.
+            treeOutline._updateModifiedNodes();
+
+            var newNode = parentNode ? parentNode.children[index] || parentNode : null;
+            if (!newNode)
+                return;
+
+            treeOutline.selectDOMNode(newNode, true);
+
+            if (wasExpanded) {
+                var newTreeItem = treeOutline.findTreeElement(newNode);
+                if (newTreeItem)
+                    newTreeItem.expand();
+            }
+        }
+
+        function commitChange(initialValue, value)
+        {
+            if (initialValue !== value)
+                node.setOuterHTML(value, selectNode);
+            else
+                return;
+        }
+
+        node.getOuterHTML(this._startEditingAsHTML.bind(this, commitChange));
+    },
+
+    _copyHTML: function()
+    {
+        this.representedObject.copyNode();
+    },
+
+    _copyXPath: function()
+    {
+        this.representedObject.copyXPath(true);
+    },
+
+    _highlightSearchResults: function()
+    {
+        if (!this._searchQuery || !this._searchHighlightsVisible)
+            return;
+        if (this._highlightResult) {
+            this._updateSearchHighlight(true);
+            return;
+        }
+
+        var text = this.listItemElement.textContent;
+        var regexObject = createPlainTextSearchRegex(this._searchQuery, "gi");
+
+        var offset = 0;
+        var match = regexObject.exec(text);
+        var matchRanges = [];
+        while (match) {
+            matchRanges.push({ offset: match.index, length: match[0].length });
+            match = regexObject.exec(text);
+        }
+
+        // Fall back for XPath, etc. matches.
+        if (!matchRanges.length)
+            matchRanges.push({ offset: 0, length: text.length });
+
+        this._highlightResult = [];
+        WebInspector.highlightSearchResults(this.listItemElement, matchRanges, this._highlightResult);
+    },
+
+    _scrollIntoView: function()
+    {
+        function scrollIntoViewCallback(object)
+        {
+            function scrollIntoView()
+            {
+                this.scrollIntoViewIfNeeded(true);
+            }
+
+            if (object)
+                object.callFunction(scrollIntoView);
+        }
+        
+        var node = /** @type {WebInspector.DOMNode} */ (this.representedObject);
+        WebInspector.RemoteObject.resolveNode(node, "", scrollIntoViewCallback);
+    },
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.ElementsTreeUpdater = function(treeOutline)
+{
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.NodeInserted, this._nodeInserted, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.NodeRemoved, this._nodeRemoved, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._attributesUpdated, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrRemoved, this._attributesUpdated, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.CharacterDataModified, this._characterDataModified, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdated, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.ChildNodeCountUpdated, this._childNodeCountUpdated, this);
+
+    this._treeOutline = treeOutline;
+    this._recentlyModifiedNodes = new Map();
+}
+
+WebInspector.ElementsTreeUpdater.prototype = {
+
+    /**
+     * @param {!WebInspector.DOMNode} node
+     * @param {boolean} isUpdated
+     * @param {WebInspector.DOMNode=} parentNode
+     */
+    _nodeModified: function(node, isUpdated, parentNode)
+    {
+        if (this._treeOutline._visible)
+            this._updateModifiedNodesSoon();
+
+        var entry = /** @type {WebInspector.ElementsTreeUpdater.UpdateEntry} */ (this._recentlyModifiedNodes.get(node));
+        if (!entry) {
+            entry = new WebInspector.ElementsTreeUpdater.UpdateEntry(isUpdated, parentNode);
+            this._recentlyModifiedNodes.put(node, entry);
+            return;
+        }
+
+        entry.isUpdated |= isUpdated;
+        if (parentNode)
+            entry.parent = parentNode;
+    },
+
+    _documentUpdated: function(event)
+    {
+        var inspectedRootDocument = event.data;
+
+        this._reset();
+
+        if (!inspectedRootDocument)
+            return;
+
+        this._treeOutline.rootDOMNode = inspectedRootDocument;
+    },
+
+    _attributesUpdated: function(event)
+    {
+        this._nodeModified(event.data.node, true);
+    },
+
+    _characterDataModified: function(event)
+    {
+        this._nodeModified(event.data, true);
+    },
+
+    _nodeInserted: function(event)
+    {
+        this._nodeModified(event.data, false, event.data.parentNode);
+    },
+
+    _nodeRemoved: function(event)
+    {
+        this._nodeModified(event.data.node, false, event.data.parent);
+    },
+
+    _childNodeCountUpdated: function(event)
+    {
+        var treeElement = this._treeOutline.findTreeElement(event.data);
+        if (treeElement)
+            treeElement.hasChildren = event.data.hasChildNodes();
+    },
+
+    _updateModifiedNodesSoon: function()
+    {
+        if (this._updateModifiedNodesTimeout)
+            return;
+        this._updateModifiedNodesTimeout = setTimeout(this._updateModifiedNodes.bind(this), 50);
+    },
+
+    _updateModifiedNodes: function()
+    {
+        if (this._updateModifiedNodesTimeout) {
+            clearTimeout(this._updateModifiedNodesTimeout);
+            delete this._updateModifiedNodesTimeout;
+        }
+
+        var updatedParentTreeElements = [];
+
+        var hidePanelWhileUpdating = this._recentlyModifiedNodes.size() > 10;
+        if (hidePanelWhileUpdating) {
+            var treeOutlineContainerElement = this._treeOutline.element.parentNode;
+            this._treeOutline.element.addStyleClass("hidden");
+            var originalScrollTop = treeOutlineContainerElement ? treeOutlineContainerElement.scrollTop : 0;
+        }
+
+        var keys = this._recentlyModifiedNodes.keys();
+        for (var i = 0, size = keys.length; i < size; ++i) {
+            var node = keys[i];
+            var entry = this._recentlyModifiedNodes.get(node);
+            var parent = entry.parent;
+
+            if (parent === this._treeOutline._rootDOMNode) {
+                // Document's children have changed, perform total update.
+                this._treeOutline.update();
+                this._treeOutline.element.removeStyleClass("hidden");
+                return;
+            }
+
+            if (entry.isUpdated) {
+                var nodeItem = this._treeOutline.findTreeElement(node);
+                if (nodeItem)
+                    nodeItem.updateTitle();
+            }
+
+            if (!parent)
+                continue;
+
+            var parentNodeItem = this._treeOutline.findTreeElement(parent);
+            if (parentNodeItem && !parentNodeItem.alreadyUpdatedChildren) {
+                parentNodeItem.updateChildren();
+                parentNodeItem.alreadyUpdatedChildren = true;
+                updatedParentTreeElements.push(parentNodeItem);
+            }
+        }
+
+        for (var i = 0; i < updatedParentTreeElements.length; ++i)
+            delete updatedParentTreeElements[i].alreadyUpdatedChildren;
+
+        if (hidePanelWhileUpdating) {
+            this._treeOutline.element.removeStyleClass("hidden");
+            if (originalScrollTop)
+                treeOutlineContainerElement.scrollTop = originalScrollTop;
+            this._treeOutline.updateSelection();
+        }
+        this._recentlyModifiedNodes.clear();
+    },
+
+    _reset: function()
+    {
+        this._treeOutline.rootDOMNode = null;
+        this._treeOutline.selectDOMNode(null, false);
+        WebInspector.domAgent.hideDOMNodeHighlight();
+        this._recentlyModifiedNodes.clear();
+    }
+}
+
+/**
+ * @constructor
+ * @param {boolean} isUpdated
+ * @param {WebInspector.DOMNode=} parent
+ */
+WebInspector.ElementsTreeUpdater.UpdateEntry = function(isUpdated, parent)
+{
+    this.isUpdated = isUpdated;
+    if (parent)
+        this.parent = parent;
+}
diff --git a/Source/devtools/front_end/EmptyView.js b/Source/devtools/front_end/EmptyView.js
new file mode 100644
index 0000000..d7a65af
--- /dev/null
+++ b/Source/devtools/front_end/EmptyView.js
@@ -0,0 +1,57 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.EmptyView = function(text)
+{
+    WebInspector.View.call(this);
+    this._text = text;
+}
+
+WebInspector.EmptyView.prototype = {
+    wasShown: function()
+    {
+        this.element.className = "storage-empty-view";
+        this.element.textContent = this._text;
+    },
+
+    set text(text)
+    {
+        this._text = text;
+        if (this.isShowing())
+            this.element.textContent = this._text;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
diff --git a/Source/devtools/front_end/EventListenersSidebarPane.js b/Source/devtools/front_end/EventListenersSidebarPane.js
new file mode 100644
index 0000000..2bd209b
--- /dev/null
+++ b/Source/devtools/front_end/EventListenersSidebarPane.js
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2007 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 Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.EventListenersSidebarPane = function()
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Event Listeners"));
+    this.bodyElement.addStyleClass("events-pane");
+
+    this.sections = [];
+
+    this.settingsSelectElement = document.createElement("select");
+    this.settingsSelectElement.className = "select-filter";
+
+    var option = document.createElement("option");
+    option.value = "all";
+    option.label = WebInspector.UIString("All Nodes");
+    this.settingsSelectElement.appendChild(option);
+
+    option = document.createElement("option");
+    option.value = "selected";
+    option.label = WebInspector.UIString("Selected Node Only");
+    this.settingsSelectElement.appendChild(option);
+
+    var filter = WebInspector.settings.eventListenersFilter.get();
+    if (filter === "all")
+        this.settingsSelectElement[0].selected = true;
+    else if (filter === "selected")
+        this.settingsSelectElement[1].selected = true;
+    this.settingsSelectElement.addEventListener("click", function(event) { event.consume() }, false);
+    this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false);
+
+    this.titleElement.appendChild(this.settingsSelectElement);
+
+    this._linkifier = new WebInspector.Linkifier();
+}
+
+WebInspector.EventListenersSidebarPane._objectGroupName = "event-listeners-sidebar-pane";
+
+WebInspector.EventListenersSidebarPane.prototype = {
+    update: function(node)
+    {
+        RuntimeAgent.releaseObjectGroup(WebInspector.EventListenersSidebarPane._objectGroupName);
+        this._linkifier.reset();
+
+        var body = this.bodyElement;
+        body.removeChildren();
+        this.sections = [];
+
+        var self = this;
+        function callback(error, eventListeners) {
+            if (error)
+                return;
+
+            var selectedNodeOnly = "selected" === WebInspector.settings.eventListenersFilter.get();
+            var sectionNames = [];
+            var sectionMap = {};
+            for (var i = 0; i < eventListeners.length; ++i) {
+                var eventListener = eventListeners[i];
+                if (selectedNodeOnly && (node.id !== eventListener.nodeId))
+                    continue;
+                eventListener.node = WebInspector.domAgent.nodeForId(eventListener.nodeId);
+                delete eventListener.nodeId; // no longer needed
+                if (/^function _inspectorCommandLineAPI_logEvent\(/.test(eventListener.handlerBody.toString()))
+                    continue; // ignore event listeners generated by monitorEvent
+                var type = eventListener.type;
+                var section = sectionMap[type];
+                if (!section) {
+                    section = new WebInspector.EventListenersSection(type, node.id, self._linkifier);
+                    sectionMap[type] = section;
+                    sectionNames.push(type);
+                    self.sections.push(section);
+                }
+                section.addListener(eventListener);
+            }
+
+            if (sectionNames.length === 0) {
+                var div = document.createElement("div");
+                div.className = "info";
+                div.textContent = WebInspector.UIString("No Event Listeners");
+                body.appendChild(div);
+                return;
+            }
+
+            sectionNames.sort();
+            for (var i = 0; i < sectionNames.length; ++i) {
+                var section = sectionMap[sectionNames[i]];
+                body.appendChild(section.element);
+            }
+        }
+
+        if (node)
+            node.eventListeners(WebInspector.EventListenersSidebarPane._objectGroupName, callback);
+        this._selectedNode = node;
+    },
+
+    willHide: function()
+    {
+        delete this._selectedNode;
+    },
+
+    _changeSetting: function()
+    {
+        var selectedOption = this.settingsSelectElement[this.settingsSelectElement.selectedIndex];
+        WebInspector.settings.eventListenersFilter.set(selectedOption.value);
+        this.update(this._selectedNode);
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.PropertiesSection}
+ */
+WebInspector.EventListenersSection = function(title, nodeId, linkifier)
+{
+    this.eventListeners = [];
+    this._nodeId = nodeId;
+    this._linkifier = linkifier;
+    WebInspector.PropertiesSection.call(this, title);
+
+    // Changed from a Properties List
+    this.propertiesElement.parentNode.removeChild(this.propertiesElement);
+    delete this.propertiesElement;
+    delete this.propertiesTreeOutline;
+
+    this._eventBars = document.createElement("div");
+    this._eventBars.className = "event-bars";
+    this.element.appendChild(this._eventBars);
+}
+
+WebInspector.EventListenersSection.prototype = {
+    addListener: function(eventListener)
+    {
+        var eventListenerBar = new WebInspector.EventListenerBar(eventListener, this._nodeId, this._linkifier);
+        this._eventBars.appendChild(eventListenerBar.element);
+    },
+
+    __proto__: WebInspector.PropertiesSection.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ObjectPropertiesSection}
+ */
+WebInspector.EventListenerBar = function(eventListener, nodeId, linkifier)
+{
+    WebInspector.ObjectPropertiesSection.call(this, WebInspector.RemoteObject.fromPrimitiveValue(""));
+
+    this.eventListener = eventListener;
+    this._nodeId = nodeId;
+    this._setNodeTitle();
+    this._setFunctionSubtitle(linkifier);
+    this.editable = false;
+    this.element.className = "event-bar"; /* Changed from "section" */
+    this.headerElement.addStyleClass("source-code");
+    this.propertiesElement.className = "event-properties properties-tree source-code"; /* Changed from "properties" */
+}
+
+WebInspector.EventListenerBar.prototype = {
+    update: function()
+    {
+        function updateWithNodeObject(nodeObject)
+        {
+            var properties = [];
+
+            if (this.eventListener.type)
+                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("type", this.eventListener.type));
+            if (typeof this.eventListener.useCapture !== "undefined")
+                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("useCapture", this.eventListener.useCapture));
+            if (typeof this.eventListener.isAttribute !== "undefined")
+                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("isAttribute", this.eventListener.isAttribute));
+            if (nodeObject)
+                properties.push(new WebInspector.RemoteObjectProperty("node", nodeObject));
+            if (typeof this.eventListener.handler !== "undefined") {
+                var remoteObject = WebInspector.RemoteObject.fromPayload(this.eventListener.handler);
+                properties.push(new WebInspector.RemoteObjectProperty("handler", remoteObject));
+            }
+            if (typeof this.eventListener.handlerBody !== "undefined")
+                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("listenerBody", this.eventListener.handlerBody));
+            if (this.eventListener.sourceName)
+                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("sourceName", this.eventListener.sourceName));
+            if (this.eventListener.location)
+                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("lineNumber", this.eventListener.location.lineNumber + 1));
+
+            this.updateProperties(properties);
+        }
+        WebInspector.RemoteObject.resolveNode(this.eventListener.node, WebInspector.EventListenersSidebarPane._objectGroupName, updateWithNodeObject.bind(this));
+    },
+
+    _setNodeTitle: function()
+    {
+        var node = this.eventListener.node;
+        if (!node)
+            return;
+
+        if (node.nodeType() === Node.DOCUMENT_NODE) {
+            this.titleElement.textContent = "document";
+            return;
+        }
+
+        if (node.id === this._nodeId) {
+            this.titleElement.textContent = node.appropriateSelectorFor();
+            return;
+        }
+
+        this.titleElement.removeChildren();
+        this.titleElement.appendChild(WebInspector.DOMPresentationUtils.linkifyNodeReference(this.eventListener.node));
+    },
+
+    _setFunctionSubtitle: function(linkifier)
+    {
+        // Requires that Function.toString() return at least the function's signature.
+        if (this.eventListener.location) {
+            this.subtitleElement.removeChildren();
+            var urlElement;
+            if (this.eventListener.location.scriptId)
+                urlElement = linkifier.linkifyRawLocation(this.eventListener.location);
+            if (!urlElement) {
+                var url = this.eventListener.sourceName;
+                var lineNumber = this.eventListener.location.lineNumber;
+                var columnNumber = 0;
+                urlElement = linkifier.linkifyLocation(url, lineNumber, columnNumber);
+            }
+            this.subtitleElement.appendChild(urlElement);
+        } else {
+            var match = this.eventListener.handlerBody.match(/function ([^\(]+?)\(/);
+            if (match)
+                this.subtitleElement.textContent = match[1];
+            else
+                this.subtitleElement.textContent = WebInspector.UIString("(anonymous function)");
+        }
+    },
+
+    __proto__: WebInspector.ObjectPropertiesSection.prototype
+}
diff --git a/Source/devtools/front_end/ExtensionAPI.js b/Source/devtools/front_end/ExtensionAPI.js
new file mode 100644
index 0000000..3d73a6a
--- /dev/null
+++ b/Source/devtools/front_end/ExtensionAPI.js
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+function defineCommonExtensionSymbols(apiPrivate)
+{
+    if (!apiPrivate.audits)
+        apiPrivate.audits = {};
+    apiPrivate.audits.Severity = {
+        Info: "info",
+        Warning: "warning",
+        Severe: "severe"
+    };
+
+    if (!apiPrivate.console)
+        apiPrivate.console = {};
+    apiPrivate.console.Severity = {
+        Debug: "debug",
+        Log: "log",
+        Warning: "warning",
+        Error: "error"
+    };
+
+    if (!apiPrivate.panels)
+        apiPrivate.panels = {};
+    apiPrivate.panels.SearchAction = {
+        CancelSearch: "cancelSearch",
+        PerformSearch: "performSearch",
+        NextSearchResult: "nextSearchResult",
+        PreviousSearchResult: "previousSearchResult"
+    };
+
+    apiPrivate.Events = {
+        AuditStarted: "audit-started-",
+        ButtonClicked: "button-clicked-",
+        ConsoleMessageAdded: "console-message-added",
+        ElementsPanelObjectSelected: "panel-objectSelected-elements",
+        NetworkRequestFinished: "network-request-finished",
+        OpenResource: "open-resource",
+        PanelSearch: "panel-search-",
+        ResourceAdded: "resource-added",
+        ResourceContentCommitted: "resource-content-committed",
+        TimelineEventRecorded: "timeline-event-recorded",
+        ViewShown: "view-shown-",
+        ViewHidden: "view-hidden-"
+    };
+
+    apiPrivate.Commands = {
+        AddAuditCategory: "addAuditCategory",
+        AddAuditResult: "addAuditResult",
+        AddConsoleMessage: "addConsoleMessage",
+        AddRequestHeaders: "addRequestHeaders",
+        CreatePanel: "createPanel",
+        CreateSidebarPane: "createSidebarPane",
+        CreateStatusBarButton: "createStatusBarButton",
+        EvaluateOnInspectedPage: "evaluateOnInspectedPage",
+        GetConsoleMessages: "getConsoleMessages",
+        GetHAR: "getHAR",
+        GetPageResources: "getPageResources",
+        GetRequestContent: "getRequestContent",
+        GetResourceContent: "getResourceContent",
+        Reload: "Reload",
+        Subscribe: "subscribe",
+        SetOpenResourceHandler: "setOpenResourceHandler",
+        SetResourceContent: "setResourceContent",
+        SetSidebarContent: "setSidebarContent",
+        SetSidebarHeight: "setSidebarHeight",
+        SetSidebarPage: "setSidebarPage",
+        ShowPanel: "showPanel",
+        StopAuditCategoryRun: "stopAuditCategoryRun",
+        Unsubscribe: "unsubscribe",
+        UpdateAuditProgress: "updateAuditProgress",
+        UpdateButton: "updateButton",
+        InspectedURLChanged: "inspectedURLChanged"
+    };
+}
+
+function injectedExtensionAPI(injectedScriptId)
+{
+
+var apiPrivate = {};
+
+defineCommonExtensionSymbols(apiPrivate);
+
+var commands = apiPrivate.Commands;
+var events = apiPrivate.Events;
+var userAction = false;
+
+// Here and below, all constructors are private to API implementation.
+// For a public type Foo, if internal fields are present, these are on
+// a private FooImpl type, an instance of FooImpl is used in a closure
+// by Foo consutrctor to re-bind publicly exported members to an instance
+// of Foo.
+
+/**
+ * @constructor
+ */
+function EventSinkImpl(type, customDispatch)
+{
+    this._type = type;
+    this._listeners = [];
+    this._customDispatch = customDispatch;
+}
+
+EventSinkImpl.prototype = {
+    addListener: function(callback)
+    {
+        if (typeof callback !== "function")
+            throw "addListener: callback is not a function";
+        if (this._listeners.length === 0)
+            extensionServer.sendRequest({ command: commands.Subscribe, type: this._type });
+        this._listeners.push(callback);
+        extensionServer.registerHandler("notify-" + this._type, this._dispatch.bind(this));
+    },
+
+    removeListener: function(callback)
+    {
+        var listeners = this._listeners;
+
+        for (var i = 0; i < listeners.length; ++i) {
+            if (listeners[i] === callback) {
+                listeners.splice(i, 1);
+                break;
+            }
+        }
+        if (this._listeners.length === 0)
+            extensionServer.sendRequest({ command: commands.Unsubscribe, type: this._type });
+    },
+
+    _fire: function()
+    {
+        var listeners = this._listeners.slice();
+        for (var i = 0; i < listeners.length; ++i)
+            listeners[i].apply(null, arguments);
+    },
+
+    _dispatch: function(request)
+    {
+         if (this._customDispatch)
+             this._customDispatch.call(this, request);
+         else
+             this._fire.apply(this, request.arguments);
+    }
+}
+
+/**
+ * @constructor
+ */
+function InspectorExtensionAPI()
+{
+    this.audits = new Audits();
+    this.inspectedWindow = new InspectedWindow();
+    this.panels = new Panels();
+    this.network = new Network();
+    defineDeprecatedProperty(this, "webInspector", "resources", "network");
+    this.timeline = new Timeline();
+    this.console = new ConsoleAPI();
+}
+
+/**
+ * @constructor
+ */
+InspectorExtensionAPI.prototype = {
+    log: function(message)
+    {
+        extensionServer.sendRequest({ command: commands.Log, message: message });
+    }
+}
+
+/**
+ * @constructor
+ */
+function ConsoleAPI()
+{
+    this.onMessageAdded = new EventSink(events.ConsoleMessageAdded);
+}
+
+ConsoleAPI.prototype = {
+    getMessages: function(callback)
+    {
+        extensionServer.sendRequest({ command: commands.GetConsoleMessages }, callback);
+    },
+
+    addMessage: function(severity, text, url, line)
+    {
+        extensionServer.sendRequest({ command: commands.AddConsoleMessage, severity: severity, text: text, url: url, line: line });
+    },
+
+    get Severity()
+    {
+        return apiPrivate.console.Severity;
+    }
+}
+
+/**
+ * @constructor
+ */
+function Network()
+{
+    function dispatchRequestEvent(message)
+    {
+        var request = message.arguments[1];
+        request.__proto__ = new Request(message.arguments[0]);
+        this._fire(request);
+    }
+    this.onRequestFinished = new EventSink(events.NetworkRequestFinished, dispatchRequestEvent);
+    defineDeprecatedProperty(this, "network", "onFinished", "onRequestFinished");
+    this.onNavigated = new EventSink(events.InspectedURLChanged);
+}
+
+Network.prototype = {
+    getHAR: function(callback)
+    {
+        function callbackWrapper(result)
+        {
+            var entries = (result && result.entries) || [];
+            for (var i = 0; i < entries.length; ++i) {
+                entries[i].__proto__ = new Request(entries[i]._requestId);
+                delete entries[i]._requestId;
+            }
+            callback(result);
+        }
+        return extensionServer.sendRequest({ command: commands.GetHAR }, callback && callbackWrapper);
+    },
+
+    addRequestHeaders: function(headers)
+    {
+        return extensionServer.sendRequest({ command: commands.AddRequestHeaders, headers: headers, extensionId: window.location.hostname });
+    }
+}
+
+/**
+ * @constructor
+ */
+function RequestImpl(id)
+{
+    this._id = id;
+}
+
+RequestImpl.prototype = {
+    getContent: function(callback)
+    {
+        function callbackWrapper(response)
+        {
+            callback(response.content, response.encoding);
+        }
+        extensionServer.sendRequest({ command: commands.GetRequestContent, id: this._id }, callback && callbackWrapper);
+    }
+}
+
+/**
+ * @constructor
+ */
+function Panels()
+{
+    var panels = {
+        elements: new ElementsPanel()
+    };
+
+    function panelGetter(name)
+    {
+        return panels[name];
+    }
+    for (var panel in panels)
+        this.__defineGetter__(panel, panelGetter.bind(null, panel));
+}
+
+Panels.prototype = {
+    create: function(title, icon, page, callback)
+    {
+        var id = "extension-panel-" + extensionServer.nextObjectId();
+        var request = {
+            command: commands.CreatePanel,
+            id: id,
+            title: title,
+            icon: icon,
+            page: page
+        };
+        extensionServer.sendRequest(request, callback && callback.bind(this, new ExtensionPanel(id)));
+    },
+
+    setOpenResourceHandler: function(callback)
+    {
+        var hadHandler = extensionServer.hasHandler(events.OpenResource);
+
+        if (!callback)
+            extensionServer.unregisterHandler(events.OpenResource);
+        else {
+            function callbackWrapper(message)
+            {
+                // Allow the panel to show itself when handling the event.
+                userAction = true;
+                try {
+                    callback.call(null, new Resource(message.resource), message.lineNumber);
+                } finally {
+                    userAction = false;
+                }
+            }
+            extensionServer.registerHandler(events.OpenResource, callbackWrapper);
+        }
+        // Only send command if we either removed an existing handler or added handler and had none before.
+        if (hadHandler === !callback)
+            extensionServer.sendRequest({ command: commands.SetOpenResourceHandler, "handlerPresent": !!callback });
+    },
+
+    get SearchAction()
+    {
+        return apiPrivate.panels.SearchAction;
+    }
+}
+
+/**
+ * @constructor
+ */
+function ExtensionViewImpl(id)
+{
+    this._id = id;
+
+    function dispatchShowEvent(message)
+    {
+        var frameIndex = message.arguments[0];
+        this._fire(window.parent.frames[frameIndex]);
+    }
+    this.onShown = new EventSink(events.ViewShown + id, dispatchShowEvent);
+    this.onHidden = new EventSink(events.ViewHidden + id);
+}
+
+/**
+ * @constructor
+ */
+function PanelWithSidebarImpl(id)
+{
+    this._id = id;
+}
+
+PanelWithSidebarImpl.prototype = {
+    createSidebarPane: function(title, callback)
+    {
+        var id = "extension-sidebar-" + extensionServer.nextObjectId();
+        var request = {
+            command: commands.CreateSidebarPane,
+            panel: this._id,
+            id: id,
+            title: title
+        };
+        function callbackWrapper()
+        {
+            callback(new ExtensionSidebarPane(id));
+        }
+        extensionServer.sendRequest(request, callback && callbackWrapper);
+    },
+
+    __proto__: ExtensionViewImpl.prototype
+}
+
+/**
+ * @constructor
+ * @extends {PanelWithSidebar}
+ */
+function ElementsPanel()
+{
+    var id = "elements";
+    PanelWithSidebar.call(this, id);
+    this.onSelectionChanged = new EventSink(events.ElementsPanelObjectSelected);
+}
+
+/**
+ * @constructor
+ * @extends {ExtensionViewImpl}
+ */
+function ExtensionPanelImpl(id)
+{
+    ExtensionViewImpl.call(this, id);
+    this.onSearch = new EventSink(events.PanelSearch + id);
+}
+
+ExtensionPanelImpl.prototype = {
+    createStatusBarButton: function(iconPath, tooltipText, disabled)
+    {
+        var id = "button-" + extensionServer.nextObjectId();
+        var request = {
+            command: commands.CreateStatusBarButton,
+            panel: this._id,
+            id: id,
+            icon: iconPath,
+            tooltip: tooltipText,
+            disabled: !!disabled
+        };
+        extensionServer.sendRequest(request);
+        return new Button(id);
+    },
+
+    show: function()
+    {
+        if (!userAction)
+            return;
+
+        var request = {
+            command: commands.ShowPanel,
+            id: this._id
+        };
+        extensionServer.sendRequest(request);
+    },
+
+    __proto__: ExtensionViewImpl.prototype
+}
+
+/**
+ * @constructor
+ * @extends {ExtensionViewImpl}
+ */
+function ExtensionSidebarPaneImpl(id)
+{
+    ExtensionViewImpl.call(this, id);
+}
+
+ExtensionSidebarPaneImpl.prototype = {
+    setHeight: function(height)
+    {
+        extensionServer.sendRequest({ command: commands.SetSidebarHeight, id: this._id, height: height });
+    },
+
+    setExpression: function(expression, rootTitle, evaluateOptions)
+    {
+        var request = {
+            command: commands.SetSidebarContent,
+            id: this._id,
+            expression: expression,
+            rootTitle: rootTitle,
+            evaluateOnPage: true,
+        };
+        if (typeof evaluateOptions === "object")
+            request.evaluateOptions = evaluateOptions;
+        extensionServer.sendRequest(request, extractCallbackArgument(arguments));
+    },
+
+    setObject: function(jsonObject, rootTitle, callback)
+    {
+        extensionServer.sendRequest({ command: commands.SetSidebarContent, id: this._id, expression: jsonObject, rootTitle: rootTitle }, callback);
+    },
+
+    setPage: function(page)
+    {
+        extensionServer.sendRequest({ command: commands.SetSidebarPage, id: this._id, page: page });
+    }
+}
+
+/**
+ * @constructor
+ */
+function ButtonImpl(id)
+{
+    this._id = id;
+    this.onClicked = new EventSink(events.ButtonClicked + id);
+}
+
+ButtonImpl.prototype = {
+    update: function(iconPath, tooltipText, disabled)
+    {
+        var request = {
+            command: commands.UpdateButton,
+            id: this._id,
+            icon: iconPath,
+            tooltip: tooltipText,
+            disabled: !!disabled
+        };
+        extensionServer.sendRequest(request);
+    }
+};
+
+/**
+ * @constructor
+ */
+function Audits()
+{
+}
+
+Audits.prototype = {
+    addCategory: function(displayName, resultCount)
+    {
+        var id = "extension-audit-category-" + extensionServer.nextObjectId();
+        if (typeof resultCount !== "undefined")
+            console.warn("Passing resultCount to audits.addCategory() is deprecated. Use AuditResult.updateProgress() instead.");
+        extensionServer.sendRequest({ command: commands.AddAuditCategory, id: id, displayName: displayName, resultCount: resultCount });
+        return new AuditCategory(id);
+    }
+}
+
+/**
+ * @constructor
+ */
+function AuditCategoryImpl(id)
+{
+    function dispatchAuditEvent(request)
+    {
+        var auditResult = new AuditResult(request.arguments[0]);
+        try {
+            this._fire(auditResult);
+        } catch (e) {
+            console.error("Uncaught exception in extension audit event handler: " + e);
+            auditResult.done();
+        }
+    }
+    this._id = id;
+    this.onAuditStarted = new EventSink(events.AuditStarted + id, dispatchAuditEvent);
+}
+
+/**
+ * @constructor
+ */
+function AuditResultImpl(id)
+{
+    this._id = id;
+
+    this.createURL = this._nodeFactory.bind(null, "url");
+    this.createSnippet = this._nodeFactory.bind(null, "snippet");
+    this.createText = this._nodeFactory.bind(null, "text");
+    this.createObject = this._nodeFactory.bind(null, "object");
+    this.createNode = this._nodeFactory.bind(null, "node");
+}
+
+AuditResultImpl.prototype = {
+    addResult: function(displayName, description, severity, details)
+    {
+        // shorthand for specifying details directly in addResult().
+        if (details && !(details instanceof AuditResultNode))
+            details = new AuditResultNode(details instanceof Array ? details : [details]);
+
+        var request = {
+            command: commands.AddAuditResult,
+            resultId: this._id,
+            displayName: displayName,
+            description: description,
+            severity: severity,
+            details: details
+        };
+        extensionServer.sendRequest(request);
+    },
+
+    createResult: function()
+    {
+        return new AuditResultNode(Array.prototype.slice.call(arguments));
+    },
+
+    updateProgress: function(worked, totalWork)
+    {
+        extensionServer.sendRequest({ command: commands.UpdateAuditProgress, resultId: this._id, progress: worked / totalWork });
+    },
+
+    done: function()
+    {
+        extensionServer.sendRequest({ command: commands.StopAuditCategoryRun, resultId: this._id });
+    },
+
+    get Severity()
+    {
+        return apiPrivate.audits.Severity;
+    },
+
+    createResourceLink: function(url, lineNumber)
+    {
+        return {
+            type: "resourceLink",
+            arguments: [url, lineNumber && lineNumber - 1]
+        };
+    },
+
+    _nodeFactory: function(type)
+    {
+        return {
+            type: type,
+            arguments: Array.prototype.slice.call(arguments, 1)
+        };
+    }
+}
+
+/**
+ * @constructor
+ */
+function AuditResultNode(contents)
+{
+    this.contents = contents;
+    this.children = [];
+    this.expanded = false;
+}
+
+AuditResultNode.prototype = {
+    addChild: function()
+    {
+        var node = new AuditResultNode(Array.prototype.slice.call(arguments));
+        this.children.push(node);
+        return node;
+    }
+};
+
+/**
+ * @constructor
+ */
+function InspectedWindow()
+{
+    function dispatchResourceEvent(message)
+    {
+        this._fire(new Resource(message.arguments[0]));
+    }
+    function dispatchResourceContentEvent(message)
+    {
+        this._fire(new Resource(message.arguments[0]), message.arguments[1]);
+    }
+    this.onResourceAdded = new EventSink(events.ResourceAdded, dispatchResourceEvent);
+    this.onResourceContentCommitted = new EventSink(events.ResourceContentCommitted, dispatchResourceContentEvent);
+}
+
+InspectedWindow.prototype = {
+    reload: function(optionsOrUserAgent)
+    {
+        var options = null;
+        if (typeof optionsOrUserAgent === "object")
+            options = optionsOrUserAgent;
+        else if (typeof optionsOrUserAgent === "string") {
+            options = { userAgent: optionsOrUserAgent };
+            console.warn("Passing userAgent as string parameter to inspectedWindow.reload() is deprecated. " +
+                         "Use inspectedWindow.reload({ userAgent: value}) instead.");
+        }
+        return extensionServer.sendRequest({ command: commands.Reload, options: options });
+    },
+
+    eval: function(expression, evaluateOptions)
+    {
+        var callback = extractCallbackArgument(arguments);
+        function callbackWrapper(result)
+        {
+            if (result.isError || result.isException)
+                callback(undefined, result);
+            else
+                callback(result.value);
+        }
+        var request = {
+            command: commands.EvaluateOnInspectedPage,
+            expression: expression
+        };
+        if (typeof evaluateOptions === "object")
+            request.evaluateOptions = evaluateOptions;
+        return extensionServer.sendRequest(request, callback && callbackWrapper);
+    },
+
+    getResources: function(callback)
+    {
+        function wrapResource(resourceData)
+        {
+            return new Resource(resourceData);
+        }
+        function callbackWrapper(resources)
+        {
+            callback(resources.map(wrapResource));
+        }
+        return extensionServer.sendRequest({ command: commands.GetPageResources }, callback && callbackWrapper);
+    }
+}
+
+/**
+ * @constructor
+ */
+function ResourceImpl(resourceData)
+{
+    this._url = resourceData.url
+    this._type = resourceData.type;
+}
+
+ResourceImpl.prototype = {
+    get url()
+    {
+        return this._url;
+    },
+
+    get type()
+    {
+        return this._type;
+    },
+
+    getContent: function(callback)
+    {
+        function callbackWrapper(response)
+        {
+            callback(response.content, response.encoding);
+        }
+
+        return extensionServer.sendRequest({ command: commands.GetResourceContent, url: this._url }, callback && callbackWrapper);
+    },
+
+    setContent: function(content, commit, callback)
+    {
+        return extensionServer.sendRequest({ command: commands.SetResourceContent, url: this._url, content: content, commit: commit }, callback);
+    }
+}
+
+/**
+ * @constructor
+ */
+function TimelineImpl()
+{
+    this.onEventRecorded = new EventSink(events.TimelineEventRecorded);
+}
+
+/**
+ * @constructor
+ */
+function ExtensionServerClient()
+{
+    this._callbacks = {};
+    this._handlers = {};
+    this._lastRequestId = 0;
+    this._lastObjectId = 0;
+
+    this.registerHandler("callback", this._onCallback.bind(this));
+
+    var channel = new MessageChannel();
+    this._port = channel.port1;
+    this._port.addEventListener("message", this._onMessage.bind(this), false);
+    this._port.start();
+
+    window.parent.postMessage("registerExtension", [ channel.port2 ], "*");
+}
+
+ExtensionServerClient.prototype = {
+    /**
+     * @param {function()=} callback
+     */
+    sendRequest: function(message, callback)
+    {
+        if (typeof callback === "function")
+            message.requestId = this._registerCallback(callback);
+        return this._port.postMessage(message);
+    },
+
+    hasHandler: function(command)
+    {
+        return !!this._handlers[command];
+    },
+
+    registerHandler: function(command, handler)
+    {
+        this._handlers[command] = handler;
+    },
+
+    unregisterHandler: function(command)
+    {
+        delete this._handlers[command];
+    },
+
+    nextObjectId: function()
+    {
+        return injectedScriptId + "_" + ++this._lastObjectId;
+    },
+
+    _registerCallback: function(callback)
+    {
+        var id = ++this._lastRequestId;
+        this._callbacks[id] = callback;
+        return id;
+    },
+
+    _onCallback: function(request)
+    {
+        if (request.requestId in this._callbacks) {
+            var callback = this._callbacks[request.requestId];
+            delete this._callbacks[request.requestId];
+            callback(request.result);
+        }
+    },
+
+    _onMessage: function(event)
+    {
+        var request = event.data;
+        var handler = this._handlers[request.command];
+        if (handler)
+            handler.call(this, request);
+    }
+}
+
+function populateInterfaceClass(interface, implementation)
+{
+    for (var member in implementation) {
+        if (member.charAt(0) === "_")
+            continue;
+        var descriptor = null;
+        // Traverse prototype chain until we find the owner.
+        for (var owner = implementation; owner && !descriptor; owner = owner.__proto__)
+            descriptor = Object.getOwnPropertyDescriptor(owner, member);
+        if (!descriptor)
+            continue;
+        if (typeof descriptor.value === "function")
+            interface[member] = descriptor.value.bind(implementation);
+        else if (typeof descriptor.get === "function")
+            interface.__defineGetter__(member, descriptor.get.bind(implementation));
+        else
+            Object.defineProperty(interface, member, descriptor);
+    }
+}
+
+function declareInterfaceClass(implConstructor)
+{
+    return function()
+    {
+        var impl = { __proto__: implConstructor.prototype };
+        implConstructor.apply(impl, arguments);
+        populateInterfaceClass(this, impl);
+    }
+}
+
+function defineDeprecatedProperty(object, className, oldName, newName)
+{
+    var warningGiven = false;
+    function getter()
+    {
+        if (!warningGiven) {
+            console.warn(className + "." + oldName + " is deprecated. Use " + className + "." + newName + " instead");
+            warningGiven = true;
+        }
+        return object[newName];
+    }
+    object.__defineGetter__(oldName, getter);
+}
+
+function extractCallbackArgument(args)
+{
+    var lastArgument = args[args.length - 1];
+    return typeof lastArgument === "function" ? lastArgument : undefined;
+}
+
+var AuditCategory = declareInterfaceClass(AuditCategoryImpl);
+var AuditResult = declareInterfaceClass(AuditResultImpl);
+var Button = declareInterfaceClass(ButtonImpl);
+var EventSink = declareInterfaceClass(EventSinkImpl);
+var ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl);
+var ExtensionSidebarPane = declareInterfaceClass(ExtensionSidebarPaneImpl);
+var PanelWithSidebar = declareInterfaceClass(PanelWithSidebarImpl);
+var Request = declareInterfaceClass(RequestImpl);
+var Resource = declareInterfaceClass(ResourceImpl);
+var Timeline = declareInterfaceClass(TimelineImpl);
+
+// extensionServer is a closure variable defined by the glue below -- make sure we fail if it's not there.
+if (!extensionServer)
+    extensionServer = new ExtensionServerClient();
+
+return new InspectorExtensionAPI();
+}
+
+/**
+ * @param {ExtensionDescriptor} extensionInfo
+ * @return {string}
+ */
+function buildExtensionAPIInjectedScript(extensionInfo)
+{
+    return "(function(injectedScriptId){ " +
+        "var extensionServer;" +
+        defineCommonExtensionSymbols.toString() + ";" +
+        injectedExtensionAPI.toString() + ";" +
+        buildPlatformExtensionAPI(extensionInfo) + ";" +
+        "platformExtensionAPI(injectedExtensionAPI(injectedScriptId));" +
+        "return {};" +
+        "})";
+}
diff --git a/Source/devtools/front_end/ExtensionAuditCategory.js b/Source/devtools/front_end/ExtensionAuditCategory.js
new file mode 100644
index 0000000..4c692c7
--- /dev/null
+++ b/Source/devtools/front_end/ExtensionAuditCategory.js
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.AuditCategory}
+ * @param {string} extensionOrigin
+ * @param {string} id
+ * @param {string} displayName
+ * @param {number=} ruleCount
+ */
+WebInspector.ExtensionAuditCategory = function(extensionOrigin, id, displayName, ruleCount)
+{
+    this._extensionOrigin = extensionOrigin;
+    this._id = id;
+    this._displayName = displayName;
+    this._ruleCount  = ruleCount;
+}
+
+WebInspector.ExtensionAuditCategory.prototype = {
+    // AuditCategory interface
+    get id()
+    {
+        return this._id;
+    },
+
+    get displayName()
+    {
+        return this._displayName;
+    },
+
+    /**
+     * @param {Array.<WebInspector.NetworkRequest>} requests
+     * @param {function(WebInspector.AuditRuleResult)} ruleResultCallback
+     * @param {function()} categoryDoneCallback
+     * @param {WebInspector.Progress} progress
+     */
+    run: function(requests, ruleResultCallback, categoryDoneCallback, progress)
+    {
+        var results = new WebInspector.ExtensionAuditCategoryResults(this, ruleResultCallback, categoryDoneCallback, progress);
+        WebInspector.extensionServer.startAuditRun(this, results);
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.ExtensionAuditCategory} category
+ * @param {function(WebInspector.AuditRuleResult)} ruleResultCallback
+ * @param {function()} categoryDoneCallback
+ * @param {WebInspector.Progress} progress
+ */
+WebInspector.ExtensionAuditCategoryResults = function(category, ruleResultCallback, categoryDoneCallback, progress)
+{
+    this._category = category;
+    this._ruleResultCallback = ruleResultCallback;
+    this._categoryDoneCallback = categoryDoneCallback;
+    this._progress = progress;
+    this._progress.setTotalWork(1);
+    this._expectedResults = category._ruleCount;
+    this._actualResults = 0;
+
+    this.id = category.id + "-" + ++WebInspector.ExtensionAuditCategoryResults._lastId;
+}
+
+WebInspector.ExtensionAuditCategoryResults.prototype = {
+    done: function()
+    {
+        WebInspector.extensionServer.stopAuditRun(this);
+        this._progress.done();
+        this._categoryDoneCallback();
+    },
+
+    addResult: function(displayName, description, severity, details)
+    {
+        var result = new WebInspector.AuditRuleResult(displayName);
+        result.addChild(description);
+        result.severity = severity;
+        if (details)
+            this._addNode(result, details);
+        this._addResult(result);
+    },
+
+    _addNode: function(parent, node)
+    {
+        var contents = WebInspector.auditFormatters.partiallyApply(WebInspector.ExtensionAuditFormatters, this, node.contents);
+        var addedNode = parent.addChild(contents, node.expanded);
+        if (node.children) {
+            for (var i = 0; i < node.children.length; ++i)
+                this._addNode(addedNode, node.children[i]);
+        }
+    },
+
+    _addResult: function(result)
+    {
+        this._ruleResultCallback(result);
+        ++this._actualResults;
+        if (typeof this._expectedResults === "number") {
+            this._progress.setWorked(this._actualResults / this._expectedResults);
+            if (this._actualResults === this._expectedResults)
+                this.done();
+        }
+    },
+
+    /**
+     * @param {number} progress
+     */
+    updateProgress: function(progress)
+    {
+        this._progress.setWorked(progress);
+    },
+
+    /**
+     * @param {string} expression
+     * @param {function(WebInspector.RemoteObject)} callback
+     */
+    evaluate: function(expression, evaluateOptions, callback)
+    {
+        /**
+         * @param {?string} error
+         * @param {?RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function onEvaluate(error, result, wasThrown)
+        {
+            if (wasThrown)
+                return;
+            var object = WebInspector.RemoteObject.fromPayload(result);
+            callback(object);
+        }
+        WebInspector.extensionServer.evaluate(expression, false, false, evaluateOptions, this._category._extensionOrigin, onEvaluate);
+    }
+}
+
+WebInspector.ExtensionAuditFormatters = {
+    /**
+     * @this {WebInspector.ExtensionAuditCategoryResults}
+     * @param {string} expression
+     * @param {string} title
+     * @param {Object} evaluateOptions
+     */
+    object: function(expression, title, evaluateOptions)
+    {
+        var parentElement = document.createElement("div");
+        function onEvaluate(remoteObject)
+        {
+            var section = new WebInspector.ObjectPropertiesSection(remoteObject, title);
+            section.expanded = true;
+            section.editable = false;
+            parentElement.appendChild(section.element);
+        }
+        this.evaluate(expression, evaluateOptions, onEvaluate);
+        return parentElement;
+    },
+
+    /**
+     * @this {WebInspector.ExtensionAuditCategoryResults}
+     * @param {string} expression
+     * @param {Object} evaluateOptions
+     */
+    node: function(expression, evaluateOptions)
+    {
+        var parentElement = document.createElement("div");
+        /**
+         * @param {?number} nodeId
+         */
+        function onNodeAvailable(nodeId)
+        {
+            if (!nodeId)
+                return;
+            var treeOutline = new WebInspector.ElementsTreeOutline(false, false, true);
+            treeOutline.rootDOMNode = WebInspector.domAgent.nodeForId(nodeId);
+            treeOutline.element.addStyleClass("outline-disclosure");
+            treeOutline.setVisible(true);
+            parentElement.appendChild(treeOutline.element);
+        }
+        /**
+         * @param {WebInspector.RemoteObject} remoteObject
+         */
+        function onEvaluate(remoteObject)
+        {
+            remoteObject.pushNodeToFrontend(onNodeAvailable);
+        }
+        this.evaluate(expression, evaluateOptions, onEvaluate);
+        return parentElement;
+    }
+}
+
+WebInspector.ExtensionAuditCategoryResults._lastId = 0;
diff --git a/Source/devtools/front_end/ExtensionPanel.js b/Source/devtools/front_end/ExtensionPanel.js
new file mode 100644
index 0000000..09a4d6b
--- /dev/null
+++ b/Source/devtools/front_end/ExtensionPanel.js
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Panel}
+ * @param {string} id
+ * @param {string} pageURL
+ */
+WebInspector.ExtensionPanel = function(id, pageURL)
+{
+    WebInspector.Panel.call(this, id);
+    this.setHideOnDetach();
+    this._statusBarItems = [];
+    var extensionView = new WebInspector.ExtensionView(id, pageURL, "extension panel");
+    extensionView.show(this.element);
+    this.setDefaultFocusedElement(extensionView.defaultFocusedElement());
+}
+
+WebInspector.ExtensionPanel.prototype = {
+    defaultFocusedElement: function()
+    {
+        return WebInspector.View.prototype.defaultFocusedElement.call(this);
+    },
+
+    get statusBarItems()
+    {
+        return this._statusBarItems;
+    },
+
+    /**
+     * @param {Element} element
+     */
+    addStatusBarItem: function(element)
+    {
+        this._statusBarItems.push(element);
+    },
+
+    searchCanceled: function(startingNewSearch)
+    {
+        WebInspector.extensionServer.notifySearchAction(this.name, WebInspector.extensionAPI.panels.SearchAction.CancelSearch);
+        WebInspector.Panel.prototype.searchCanceled.apply(this, arguments);
+    },
+
+    /**
+     * @param {string} query
+     */
+    performSearch: function(query)
+    {
+        WebInspector.extensionServer.notifySearchAction(this.name, WebInspector.extensionAPI.panels.SearchAction.PerformSearch, query);
+        WebInspector.Panel.prototype.performSearch.apply(this, arguments);
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        WebInspector.extensionServer.notifySearchAction(this.name, WebInspector.extensionAPI.panels.SearchAction.NextSearchResult);
+        WebInspector.Panel.prototype.jumpToNextSearchResult.call(this);
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        WebInspector.extensionServer.notifySearchAction(this.name, WebInspector.extensionAPI.panels.SearchAction.PreviousSearchResult);
+        WebInspector.Panel.prototype.jumpToPreviousSearchResult.call(this);
+    },
+
+    __proto__: WebInspector.Panel.prototype
+}
+
+/**
+ * @constructor
+ * @param {string} id
+ * @param {string} iconURL
+ * @param {string=} tooltip
+ * @param {boolean=} disabled
+ */
+WebInspector.ExtensionButton = function(id, iconURL, tooltip, disabled)
+{
+    this._id = id;
+    this.element = document.createElement("button");
+    this.element.className = "status-bar-item extension";
+    this.element.addEventListener("click", this._onClicked.bind(this), false);
+    this.update(iconURL, tooltip, disabled);
+}
+
+WebInspector.ExtensionButton.prototype = {
+    /**
+     * @param {string} iconURL
+     * @param {string=} tooltip
+     * @param {boolean=} disabled
+     */
+    update: function(iconURL, tooltip, disabled)
+    {
+        if (typeof iconURL === "string")
+            this.element.style.backgroundImage = "url(" + iconURL + ")";
+        if (typeof tooltip === "string")
+            this.element.title = tooltip;
+        if (typeof disabled === "boolean")
+            this.element.disabled = disabled;
+    },
+
+    _onClicked: function()
+    {
+        WebInspector.extensionServer.notifyButtonClicked(this._id);
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ * @param {string} title
+ * @param {string} id
+ */
+WebInspector.ExtensionSidebarPane = function(title, id)
+{
+    WebInspector.SidebarPane.call(this, title);
+    this.setHideOnDetach();
+    this._id = id;
+}
+
+WebInspector.ExtensionSidebarPane.prototype = {
+    /**
+     * @param {Object} object
+     * @param {string} title
+     * @param {function(?string=)} callback
+     */
+    setObject: function(object, title, callback)
+    {
+        this._createObjectPropertiesView();
+        this._setObject(WebInspector.RemoteObject.fromLocalObject(object), title, callback);
+    },
+
+    /**
+     * @param {string} expression
+     * @param {string} title
+     * @param {function(?string=)} callback
+     */
+    setExpression: function(expression, title, evaluateOptions, securityOrigin, callback)
+    {
+        this._createObjectPropertiesView();
+        return WebInspector.extensionServer.evaluate(expression, true, false, evaluateOptions, securityOrigin, this._onEvaluate.bind(this, title, callback));
+    },
+
+    /**
+     * @param {string} url
+     */
+    setPage: function(url)
+    {
+        if (this._objectPropertiesView) {
+            this._objectPropertiesView.detach();
+            delete this._objectPropertiesView;
+        }
+        if (this._extensionView)
+            this._extensionView.detach(true);
+
+        this._extensionView = new WebInspector.ExtensionView(this._id, url, "extension fill");
+        this._extensionView.show(this.bodyElement);
+
+        if (!this.bodyElement.style.height)
+            this.setHeight("150px");
+    },
+
+    /**
+     * @param {string} height
+     */
+    setHeight: function(height)
+    {
+        this.bodyElement.style.height = height;
+    },
+
+    /**
+     * @param {string} title
+     * @param {function(?string=)} callback
+     * @param {?Protocol.Error} error
+     * @param {RuntimeAgent.RemoteObject} result
+     * @param {boolean=} wasThrown
+     */
+    _onEvaluate: function(title, callback, error, result, wasThrown)
+    {
+        if (error)
+            callback(error.toString());
+        else
+            this._setObject(WebInspector.RemoteObject.fromPayload(result), title, callback);
+    },
+
+    _createObjectPropertiesView: function()
+    {
+        if (this._objectPropertiesView)
+            return;
+        if (this._extensionView) {
+            this._extensionView.detach(true);
+            delete this._extensionView;
+        }
+        this._objectPropertiesView = new WebInspector.ExtensionNotifierView(this._id);
+        this._objectPropertiesView.show(this.bodyElement);
+    },
+
+    /**
+     * @param {WebInspector.RemoteObject} object
+     * @param {string} title
+     * @param {function(?string=)} callback
+     */
+    _setObject: function(object, title, callback)
+    {
+        // This may only happen if setPage() was called while we were evaluating the expression.
+        if (!this._objectPropertiesView) {
+            callback("operation cancelled");
+            return;
+        }
+        this._objectPropertiesView.element.removeChildren();
+        var section = new WebInspector.ObjectPropertiesSection(object, title);
+        if (!title)
+            section.headerElement.addStyleClass("hidden");
+        section.expanded = true;
+        section.editable = false;
+        this._objectPropertiesView.element.appendChild(section.element);
+        callback();
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
diff --git a/Source/devtools/front_end/ExtensionRegistryStub.js b/Source/devtools/front_end/ExtensionRegistryStub.js
new file mode 100644
index 0000000..2580880
--- /dev/null
+++ b/Source/devtools/front_end/ExtensionRegistryStub.js
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+if (!window.InspectorExtensionRegistry) {
+
+/**
+ * @constructor
+ */
+WebInspector.InspectorExtensionRegistryStub = function()
+{
+}
+
+WebInspector.InspectorExtensionRegistryStub.prototype = {
+    getExtensionsAsync: function()
+    {
+    }
+}
+
+var InspectorExtensionRegistry = new WebInspector.InspectorExtensionRegistryStub();
+
+}
diff --git a/Source/devtools/front_end/ExtensionServer.js b/Source/devtools/front_end/ExtensionServer.js
new file mode 100644
index 0000000..3dab484
--- /dev/null
+++ b/Source/devtools/front_end/ExtensionServer.js
@@ -0,0 +1,874 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.ExtensionServer = function()
+{
+    this._clientObjects = {};
+    this._handlers = {};
+    this._subscribers = {};
+    this._subscriptionStartHandlers = {};
+    this._subscriptionStopHandlers = {};
+    this._extraHeaders = {};
+    this._requests = {};
+    this._lastRequestId = 0;
+    this._registeredExtensions = {};
+    this._status = new WebInspector.ExtensionStatus();
+
+    var commands = WebInspector.extensionAPI.Commands;
+
+    this._registerHandler(commands.AddAuditCategory, this._onAddAuditCategory.bind(this));
+    this._registerHandler(commands.AddAuditResult, this._onAddAuditResult.bind(this));
+    this._registerHandler(commands.AddConsoleMessage, this._onAddConsoleMessage.bind(this));
+    this._registerHandler(commands.AddRequestHeaders, this._onAddRequestHeaders.bind(this));
+    this._registerHandler(commands.CreatePanel, this._onCreatePanel.bind(this));
+    this._registerHandler(commands.CreateSidebarPane, this._onCreateSidebarPane.bind(this));
+    this._registerHandler(commands.CreateStatusBarButton, this._onCreateStatusBarButton.bind(this));
+    this._registerHandler(commands.EvaluateOnInspectedPage, this._onEvaluateOnInspectedPage.bind(this));
+    this._registerHandler(commands.GetHAR, this._onGetHAR.bind(this));
+    this._registerHandler(commands.GetConsoleMessages, this._onGetConsoleMessages.bind(this));
+    this._registerHandler(commands.GetPageResources, this._onGetPageResources.bind(this));
+    this._registerHandler(commands.GetRequestContent, this._onGetRequestContent.bind(this));
+    this._registerHandler(commands.GetResourceContent, this._onGetResourceContent.bind(this));
+    this._registerHandler(commands.Log, this._onLog.bind(this));
+    this._registerHandler(commands.Reload, this._onReload.bind(this));
+    this._registerHandler(commands.SetOpenResourceHandler, this._onSetOpenResourceHandler.bind(this));
+    this._registerHandler(commands.SetResourceContent, this._onSetResourceContent.bind(this));
+    this._registerHandler(commands.SetSidebarHeight, this._onSetSidebarHeight.bind(this));
+    this._registerHandler(commands.SetSidebarContent, this._onSetSidebarContent.bind(this));
+    this._registerHandler(commands.SetSidebarPage, this._onSetSidebarPage.bind(this));
+    this._registerHandler(commands.ShowPanel, this._onShowPanel.bind(this));
+    this._registerHandler(commands.StopAuditCategoryRun, this._onStopAuditCategoryRun.bind(this));
+    this._registerHandler(commands.Subscribe, this._onSubscribe.bind(this));
+    this._registerHandler(commands.Unsubscribe, this._onUnsubscribe.bind(this));
+    this._registerHandler(commands.UpdateButton, this._onUpdateButton.bind(this));
+    this._registerHandler(commands.UpdateAuditProgress, this._onUpdateAuditProgress.bind(this));
+
+    window.addEventListener("message", this._onWindowMessage.bind(this), false);
+}
+
+WebInspector.ExtensionServer.prototype = {
+    hasExtensions: function()
+    {
+        return !!Object.keys(this._registeredExtensions).length;
+    },
+
+    notifySearchAction: function(panelId, action, searchString)
+    {
+        this._postNotification(WebInspector.extensionAPI.Events.PanelSearch + panelId, action, searchString);
+    },
+
+    notifyViewShown: function(identifier, frameIndex)
+    {
+        this._postNotification(WebInspector.extensionAPI.Events.ViewShown + identifier, frameIndex);
+    },
+
+    notifyViewHidden: function(identifier)
+    {
+        this._postNotification(WebInspector.extensionAPI.Events.ViewHidden + identifier);
+    },
+
+    notifyButtonClicked: function(identifier)
+    {
+        this._postNotification(WebInspector.extensionAPI.Events.ButtonClicked + identifier);
+    },
+
+    _inspectedURLChanged: function(event)
+    {
+        this._requests = {};
+        var url = event.data;
+        this._postNotification(WebInspector.extensionAPI.Events.InspectedURLChanged, url);
+    },
+
+    startAuditRun: function(category, auditRun)
+    {
+        this._clientObjects[auditRun.id] = auditRun;
+        this._postNotification("audit-started-" + category.id, auditRun.id);
+    },
+
+    stopAuditRun: function(auditRun)
+    {
+        delete this._clientObjects[auditRun.id];
+    },
+
+    /**
+     * @param {...*} vararg
+     */
+    _postNotification: function(type, vararg)
+    {
+        var subscribers = this._subscribers[type];
+        if (!subscribers)
+            return;
+        var message = {
+            command: "notify-" + type,
+            arguments: Array.prototype.slice.call(arguments, 1)
+        };
+        for (var i = 0; i < subscribers.length; ++i)
+            subscribers[i].postMessage(message);
+    },
+
+    _onSubscribe: function(message, port)
+    {
+        var subscribers = this._subscribers[message.type];
+        if (subscribers)
+            subscribers.push(port);
+        else {
+            this._subscribers[message.type] = [ port ];
+            if (this._subscriptionStartHandlers[message.type])
+                this._subscriptionStartHandlers[message.type]();
+        }
+    },
+
+    _onUnsubscribe: function(message, port)
+    {
+        var subscribers = this._subscribers[message.type];
+        if (!subscribers)
+            return;
+        subscribers.remove(port);
+        if (!subscribers.length) {
+            delete this._subscribers[message.type];
+            if (this._subscriptionStopHandlers[message.type])
+                this._subscriptionStopHandlers[message.type]();
+        }
+    },
+
+    _onAddRequestHeaders: function(message)
+    {
+        var id = message.extensionId;
+        if (typeof id !== "string")
+            return this._status.E_BADARGTYPE("extensionId", typeof id, "string");
+        var extensionHeaders = this._extraHeaders[id];
+        if (!extensionHeaders) {
+            extensionHeaders = {};
+            this._extraHeaders[id] = extensionHeaders;
+        }
+        for (var name in message.headers)
+            extensionHeaders[name] = message.headers[name];
+        var allHeaders = /** @type NetworkAgent.Headers */ ({});
+        for (var extension in this._extraHeaders) {
+            var headers = this._extraHeaders[extension];
+            for (name in headers) {
+                if (typeof headers[name] === "string")
+                    allHeaders[name] = headers[name];
+            }
+        }
+        NetworkAgent.setExtraHTTPHeaders(allHeaders);
+    },
+
+    _onCreatePanel: function(message, port)
+    {
+        var id = message.id;
+        // The ids are generated on the client API side and must be unique, so the check below
+        // shouldn't be hit unless someone is bypassing the API.
+        if (id in this._clientObjects || id in WebInspector.panels)
+            return this._status.E_EXISTS(id);
+
+        var page = this._expandResourcePath(port._extensionOrigin, message.page);
+        var panelDescriptor = new WebInspector.PanelDescriptor(id, message.title, undefined, undefined, new WebInspector.ExtensionPanel(id, page));
+        panelDescriptor.setIconURL(this._expandResourcePath(port._extensionOrigin, message.icon));
+        this._clientObjects[id] = panelDescriptor.panel();
+        WebInspector.inspectorView.addPanel(panelDescriptor);
+        return this._status.OK();
+    },
+
+    _onShowPanel: function(message)
+    {
+        // Note: WebInspector.showPanel already sanitizes input.
+        WebInspector.showPanel(message.id);
+    },
+
+    _onCreateStatusBarButton: function(message, port)
+    {
+        var panel = this._clientObjects[message.panel];
+        if (!panel || !(panel instanceof WebInspector.ExtensionPanel))
+            return this._status.E_NOTFOUND(message.panel);
+        var button = new WebInspector.ExtensionButton(message.id, this._expandResourcePath(port._extensionOrigin, message.icon), message.tooltip, message.disabled);
+        this._clientObjects[message.id] = button;
+        panel.addStatusBarItem(button.element);
+        return this._status.OK();
+    },
+
+    _onUpdateButton: function(message, port)
+    {
+        var button = this._clientObjects[message.id];
+        if (!button || !(button instanceof WebInspector.ExtensionButton))
+            return this._status.E_NOTFOUND(message.id);
+        button.update(this._expandResourcePath(port._extensionOrigin, message.icon), message.tooltip, message.disabled);
+        return this._status.OK();
+    },
+
+    _onCreateSidebarPane: function(message)
+    {
+        var panel = WebInspector.panel(message.panel);
+        if (!panel)
+            return this._status.E_NOTFOUND(message.panel);
+        if (!panel.addExtensionSidebarPane)
+            return this._status.E_NOTSUPPORTED();
+        var id = message.id;
+        var sidebar = new WebInspector.ExtensionSidebarPane(message.title, message.id);
+        this._clientObjects[id] = sidebar;
+        panel.addExtensionSidebarPane(id, sidebar);
+
+        return this._status.OK();
+    },
+
+    _onSetSidebarHeight: function(message)
+    {
+        var sidebar = this._clientObjects[message.id];
+        if (!sidebar)
+            return this._status.E_NOTFOUND(message.id);
+        sidebar.setHeight(message.height);
+        return this._status.OK();
+    },
+
+    _onSetSidebarContent: function(message, port)
+    {
+        var sidebar = this._clientObjects[message.id];
+        if (!sidebar)
+            return this._status.E_NOTFOUND(message.id);
+        function callback(error)
+        {
+            var result = error ? this._status.E_FAILED(error) : this._status.OK();
+            this._dispatchCallback(message.requestId, port, result);
+        }
+        if (message.evaluateOnPage)
+            return sidebar.setExpression(message.expression, message.rootTitle, message.evaluateOptions, port._extensionOrigin, callback.bind(this));
+        sidebar.setObject(message.expression, message.rootTitle, callback.bind(this));
+    },
+
+    _onSetSidebarPage: function(message, port)
+    {
+        var sidebar = this._clientObjects[message.id];
+        if (!sidebar)
+            return this._status.E_NOTFOUND(message.id);
+        sidebar.setPage(this._expandResourcePath(port._extensionOrigin, message.page));
+    },
+
+    _onSetOpenResourceHandler: function(message, port)
+    {
+        var name = this._registeredExtensions[port._extensionOrigin].name || ("Extension " + port._extensionOrigin);
+        if (message.handlerPresent)
+            WebInspector.openAnchorLocationRegistry.registerHandler(name, this._handleOpenURL.bind(this, port));
+        else
+            WebInspector.openAnchorLocationRegistry.unregisterHandler(name);
+    },
+
+    _handleOpenURL: function(port, details)
+    {
+        var url = /** @type {string} */ (details.url);
+        var contentProvider = WebInspector.workspace.uiSourceCodeForOriginURL(url) || WebInspector.resourceForURL(url);
+        if (!contentProvider)
+            return false;
+            
+        var lineNumber = details.lineNumber;
+        if (typeof lineNumber === "number")
+            lineNumber += 1;
+        port.postMessage({
+            command: "open-resource",
+            resource: this._makeResource(contentProvider),
+            lineNumber: lineNumber
+        });
+        return true;
+    },
+
+    _onLog: function(message)
+    {
+        WebInspector.log(message.message);
+    },
+
+    _onReload: function(message)
+    {
+        var options = /** @type ExtensionReloadOptions */ (message.options || {});
+        NetworkAgent.setUserAgentOverride(typeof options.userAgent === "string" ? options.userAgent : "");
+        var injectedScript;
+        if (options.injectedScript)
+            injectedScript = "(function(){" + options.injectedScript + "})()";
+        PageAgent.reload(!!options.ignoreCache, injectedScript);
+        return this._status.OK();
+    },
+
+    _onEvaluateOnInspectedPage: function(message, port)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {RuntimeAgent.RemoteObject} resultPayload
+         * @param {boolean=} wasThrown
+         */
+        function callback(error, resultPayload, wasThrown)
+        {
+            var result;
+            if (error)
+                result = this._status.E_PROTOCOLERROR(error.toString());
+            else if (wasThrown)
+                result = { isException: true, value: resultPayload.description };
+            else
+                result = { value: resultPayload.value };
+      
+            this._dispatchCallback(message.requestId, port, result);
+        }
+        return this.evaluate(message.expression, true, true, message.evaluateOptions, port._extensionOrigin, callback.bind(this));
+    },
+
+    _onGetConsoleMessages: function()
+    {
+        return WebInspector.console.messages.map(this._makeConsoleMessage);
+    },
+
+    _onAddConsoleMessage: function(message)
+    {
+        function convertSeverity(level)
+        {
+            switch (level) {
+                case WebInspector.extensionAPI.console.Severity.Log:
+                    return WebInspector.ConsoleMessage.MessageLevel.Log;
+                case WebInspector.extensionAPI.console.Severity.Warning:
+                    return WebInspector.ConsoleMessage.MessageLevel.Warning;
+                case WebInspector.extensionAPI.console.Severity.Error:
+                    return WebInspector.ConsoleMessage.MessageLevel.Error;
+                case WebInspector.extensionAPI.console.Severity.Debug:
+                    return WebInspector.ConsoleMessage.MessageLevel.Debug;
+            }
+        }
+        var level = convertSeverity(message.severity);
+        if (!level)
+            return this._status.E_BADARG("message.severity", message.severity);
+
+        var consoleMessage = WebInspector.ConsoleMessage.create(
+            WebInspector.ConsoleMessage.MessageSource.JS,
+            level,
+            message.text,
+            WebInspector.ConsoleMessage.MessageType.Log,
+            message.url,
+            message.line);
+        WebInspector.console.addMessage(consoleMessage);
+    },
+
+    _makeConsoleMessage: function(message)
+    {
+        function convertLevel(level)
+        {
+            if (!level)
+                return;
+            switch (level) {
+                case WebInspector.ConsoleMessage.MessageLevel.Log:
+                    return WebInspector.extensionAPI.console.Severity.Log;
+                case WebInspector.ConsoleMessage.MessageLevel.Warning:
+                    return WebInspector.extensionAPI.console.Severity.Warning;
+                case WebInspector.ConsoleMessage.MessageLevel.Error:
+                    return WebInspector.extensionAPI.console.Severity.Error;
+                case WebInspector.ConsoleMessage.MessageLevel.Debug:
+                    return WebInspector.extensionAPI.console.Severity.Debug;
+                default:
+                    return WebInspector.extensionAPI.console.Severity.Log;
+            }
+        }
+        var result = {
+            severity: convertLevel(message.level),
+            text: message.text,
+        };
+        if (message.url)
+            result.url = message.url;
+        if (message.line)
+            result.line = message.line;
+        return result;
+    },
+
+    _onGetHAR: function()
+    {
+        var requests = WebInspector.networkLog.requests;
+        var harLog = (new WebInspector.HARLog(requests)).build();
+        for (var i = 0; i < harLog.entries.length; ++i)
+            harLog.entries[i]._requestId = this._requestId(requests[i]);
+        return harLog;
+    },
+
+    /**
+     * @param {WebInspector.ContentProvider} contentProvider
+     */
+    _makeResource: function(contentProvider)
+    {
+        return {
+            url: contentProvider.contentURL(),
+            type: contentProvider.contentType().name()
+        };
+    },
+
+    _onGetPageResources: function()
+    {
+        var resources = {};
+
+        function pushResourceData(contentProvider)
+        {
+            if (!resources[contentProvider.contentURL()])
+                resources[contentProvider.contentURL()] = this._makeResource(contentProvider);
+        }
+        var uiSourceCodes = WebInspector.workspace.uiSourceCodesForProjectType(WebInspector.projectTypes.Network);
+        uiSourceCodes.forEach(pushResourceData.bind(this));
+        WebInspector.resourceTreeModel.forAllResources(pushResourceData.bind(this));
+        return Object.values(resources);
+    },
+
+    /**
+     * @param {WebInspector.ContentProvider} contentProvider
+     */
+    _getResourceContent: function(contentProvider, message, port)
+    {
+        /**
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function onContentAvailable(content, contentEncoded, mimeType)
+        {
+            var response = {
+                encoding: contentEncoded ? "base64" : "",
+                content: content
+            };
+            this._dispatchCallback(message.requestId, port, response);
+        }
+        contentProvider.requestContent(onContentAvailable.bind(this));
+    },
+
+    _onGetRequestContent: function(message, port)
+    {
+        var request = this._requestById(message.id);
+        if (!request)
+            return this._status.E_NOTFOUND(message.id);
+        this._getResourceContent(request, message, port);
+    },
+
+    _onGetResourceContent: function(message, port)
+    {
+        var url = /** @type {string} */ (message.url);
+        var contentProvider = WebInspector.workspace.uiSourceCodeForOriginURL(url) || WebInspector.resourceForURL(url);
+        if (!contentProvider)
+            return this._status.E_NOTFOUND(url);
+        this._getResourceContent(contentProvider, message, port);
+    },
+
+    _onSetResourceContent: function(message, port)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         */
+        function callbackWrapper(error)
+        {
+            var response = error ? this._status.E_FAILED(error) : this._status.OK();
+            this._dispatchCallback(message.requestId, port, response);
+        }
+
+        var url = /** @type {string} */ (message.url);
+        var uiSourceCode = WebInspector.workspace.uiSourceCodeForOriginURL(url);
+        if (!uiSourceCode) {
+            var resource = WebInspector.resourceTreeModel.resourceForURL(url);
+            if (!resource)
+                return this._status.E_NOTFOUND(url);
+            return this._status.E_NOTSUPPORTED("Resource is not editable")
+        }
+        uiSourceCode.setWorkingCopy(message.content);
+        if (message.commit)
+            uiSourceCode.commitWorkingCopy(callbackWrapper.bind(this));
+        else
+            callbackWrapper.call(this, null);
+    },
+
+    _requestId: function(request)
+    {
+        if (!request._extensionRequestId) {
+            request._extensionRequestId = ++this._lastRequestId;
+            this._requests[request._extensionRequestId] = request;
+        }
+        return request._extensionRequestId;
+    },
+
+    _requestById: function(id)
+    {
+        return this._requests[id];
+    },
+
+    _onAddAuditCategory: function(message, port)
+    {
+        var category = new WebInspector.ExtensionAuditCategory(port._extensionOrigin, message.id, message.displayName, message.resultCount);
+        if (WebInspector.panel("audits").getCategory(category.id))
+            return this._status.E_EXISTS(category.id);
+        this._clientObjects[message.id] = category;
+        WebInspector.panel("audits").addCategory(category);
+    },
+
+    _onAddAuditResult: function(message)
+    {
+        var auditResult = this._clientObjects[message.resultId];
+        if (!auditResult)
+            return this._status.E_NOTFOUND(message.resultId);
+        try {
+            auditResult.addResult(message.displayName, message.description, message.severity, message.details);
+        } catch (e) {
+            return e;
+        }
+        return this._status.OK();
+    },
+
+    _onUpdateAuditProgress: function(message)
+    {
+        var auditResult = this._clientObjects[message.resultId];
+        if (!auditResult)
+            return this._status.E_NOTFOUND(message.resultId);
+        auditResult.updateProgress(Math.min(Math.max(0, message.progress), 1));
+    },
+
+    _onStopAuditCategoryRun: function(message)
+    {
+        var auditRun = this._clientObjects[message.resultId];
+        if (!auditRun)
+            return this._status.E_NOTFOUND(message.resultId);
+        auditRun.done();
+    },
+
+    _dispatchCallback: function(requestId, port, result)
+    {
+        if (requestId)
+            port.postMessage({ command: "callback", requestId: requestId, result: result });
+    },
+
+    initExtensions: function()
+    {
+        this._registerAutosubscriptionHandler(WebInspector.extensionAPI.Events.ConsoleMessageAdded,
+            WebInspector.console, WebInspector.ConsoleModel.Events.MessageAdded, this._notifyConsoleMessageAdded);
+        this._registerAutosubscriptionHandler(WebInspector.extensionAPI.Events.NetworkRequestFinished,
+            WebInspector.networkManager, WebInspector.NetworkManager.EventTypes.RequestFinished, this._notifyRequestFinished);
+        this._registerAutosubscriptionHandler(WebInspector.extensionAPI.Events.ResourceAdded,
+            WebInspector.workspace,
+            WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded,
+            this._notifyResourceAdded);
+        this._registerAutosubscriptionHandler(WebInspector.extensionAPI.Events.ElementsPanelObjectSelected,
+            WebInspector.notifications,
+            WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged,
+            this._notifyElementsSelectionChanged);
+        this._registerAutosubscriptionHandler(WebInspector.extensionAPI.Events.ResourceContentCommitted,
+            WebInspector.workspace,
+            WebInspector.Workspace.Events.UISourceCodeContentCommitted,
+            this._notifyUISourceCodeContentCommitted);
+
+        function onTimelineSubscriptionStarted()
+        {
+            WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded,
+                this._notifyTimelineEventRecorded, this);
+            WebInspector.timelineManager.start();
+        }
+        function onTimelineSubscriptionStopped()
+        {
+            WebInspector.timelineManager.stop();
+            WebInspector.timelineManager.removeEventListener(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded,
+                this._notifyTimelineEventRecorded, this);
+        }
+        this._registerSubscriptionHandler(WebInspector.extensionAPI.Events.TimelineEventRecorded,
+            onTimelineSubscriptionStarted.bind(this), onTimelineSubscriptionStopped.bind(this));
+
+        WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged,
+            this._inspectedURLChanged, this);
+        this._initDone = true;
+        if (this._pendingExtensions) {
+            this._pendingExtensions.forEach(this._innerAddExtension, this);
+            delete this._pendingExtensions;
+        }
+        InspectorExtensionRegistry.getExtensionsAsync();
+    },
+
+    _notifyConsoleMessageAdded: function(event)
+    {
+        this._postNotification(WebInspector.extensionAPI.Events.ConsoleMessageAdded, this._makeConsoleMessage(event.data));
+    },
+
+    _notifyResourceAdded: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        this._postNotification(WebInspector.extensionAPI.Events.ResourceAdded, this._makeResource(uiSourceCode));
+    },
+
+    _notifyUISourceCodeContentCommitted: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode);
+        var content = /** @type {string} */ (event.data.content);
+        this._postNotification(WebInspector.extensionAPI.Events.ResourceContentCommitted, this._makeResource(uiSourceCode), content);
+    },
+
+    _notifyRequestFinished: function(event)
+    {
+        var request = /** @type {WebInspector.NetworkRequest} */ (event.data);
+        this._postNotification(WebInspector.extensionAPI.Events.NetworkRequestFinished, this._requestId(request), (new WebInspector.HAREntry(request)).build());
+    },
+
+    _notifyElementsSelectionChanged: function()
+    {
+        this._postNotification(WebInspector.extensionAPI.Events.ElementsPanelObjectSelected);
+    },
+
+    _notifyTimelineEventRecorded: function(event)
+    {
+        this._postNotification(WebInspector.extensionAPI.Events.TimelineEventRecorded, event.data);
+    },
+
+    /**
+     * @param {Array.<ExtensionDescriptor>} extensions
+     */
+    _addExtensions: function(extensions)
+    {
+        extensions.forEach(this._addExtension, this);
+    },
+
+    /**
+     * @param {ExtensionDescriptor} extensionInfo
+     */
+    _addExtension: function(extensionInfo)
+    {
+        if (this._initDone) {
+            this._innerAddExtension(extensionInfo);
+            return;
+        }
+        if (this._pendingExtensions)
+            this._pendingExtensions.push(extensionInfo);
+        else
+            this._pendingExtensions = [extensionInfo];
+    },
+
+    /**
+     * @param {ExtensionDescriptor} extensionInfo
+     */
+    _innerAddExtension: function(extensionInfo)
+    {
+        const urlOriginRegExp = new RegExp("([^:]+:\/\/[^/]*)\/"); // Can't use regexp literal here, MinJS chokes on it.
+        var startPage = extensionInfo.startPage;
+        var name = extensionInfo.name;
+
+        try {
+            var originMatch = urlOriginRegExp.exec(startPage);
+            if (!originMatch) {
+                console.error("Skipping extension with invalid URL: " + startPage);
+                return false;
+            }
+            var extensionOrigin = originMatch[1];
+            if (!this._registeredExtensions[extensionOrigin]) {
+                // See ExtensionAPI.js and ExtensionCommon.js for details.
+                InspectorFrontendHost.setInjectedScriptForOrigin(extensionOrigin, buildExtensionAPIInjectedScript(extensionInfo));
+                this._registeredExtensions[extensionOrigin] = { name: name };
+            }
+            var iframe = document.createElement("iframe");
+            iframe.src = startPage;
+            iframe.style.display = "none";
+            document.body.appendChild(iframe);
+        } catch (e) {
+            console.error("Failed to initialize extension " + startPage + ":" + e);
+            return false;
+        }
+        return true;
+    },
+
+    _onWindowMessage: function(event)
+    {
+        if (event.data === "registerExtension")
+            this._registerExtension(event.origin, event.ports[0]);
+    },
+
+    _registerExtension: function(origin, port)
+    {
+        if (!this._registeredExtensions.hasOwnProperty(origin)) {
+            if (origin !== window.location.origin) // Just ignore inspector frames.
+                console.error("Ignoring unauthorized client request from " + origin);
+            return;
+        }
+        port._extensionOrigin = origin;
+        port.addEventListener("message", this._onmessage.bind(this), false);
+        port.start();
+    },
+
+    _onmessage: function(event)
+    {
+        var message = event.data;
+        var result;
+
+        if (message.command in this._handlers)
+            result = this._handlers[message.command](message, event.target);
+        else
+            result = this._status.E_NOTSUPPORTED(message.command);
+
+        if (result && message.requestId)
+            this._dispatchCallback(message.requestId, event.target, result);
+    },
+
+    _registerHandler: function(command, callback)
+    {
+        console.assert(command);
+        this._handlers[command] = callback;
+    },
+
+    _registerSubscriptionHandler: function(eventTopic, onSubscribeFirst, onUnsubscribeLast)
+    {
+        this._subscriptionStartHandlers[eventTopic] =  onSubscribeFirst;
+        this._subscriptionStopHandlers[eventTopic] =  onUnsubscribeLast;
+    },
+
+    _registerAutosubscriptionHandler: function(eventTopic, eventTarget, frontendEventType, handler)
+    {
+        this._registerSubscriptionHandler(eventTopic,
+            eventTarget.addEventListener.bind(eventTarget, frontendEventType, handler, this),
+            eventTarget.removeEventListener.bind(eventTarget, frontendEventType, handler, this));
+    },
+
+    _expandResourcePath: function(extensionPath, resourcePath)
+    {
+        if (!resourcePath)
+            return;
+        return extensionPath + this._normalizePath(resourcePath);
+    },
+
+    _normalizePath: function(path)
+    {
+        var source = path.split("/");
+        var result = [];
+
+        for (var i = 0; i < source.length; ++i) {
+            if (source[i] === ".")
+                continue;
+            // Ignore empty path components resulting from //, as well as a leading and traling slashes.
+            if (source[i] === "")
+                continue;
+            if (source[i] === "..")
+                result.pop();
+            else
+                result.push(source[i]);
+        }
+        return "/" + result.join("/");
+    },
+
+    /**
+     * @param {string} expression
+     * @param {boolean} exposeCommandLineAPI
+     * @param {boolean} returnByValue
+     * @param {Object} options
+     * @param {string} securityOrigin
+     * @param {function(?string, ?RuntimeAgent.RemoteObject, boolean=)} callback
+     */
+    evaluate: function(expression, exposeCommandLineAPI, returnByValue, options, securityOrigin, callback) 
+    {
+        var contextId;
+        if (typeof options === "object") {
+
+            function resolveURLToFrame(url)
+            {
+                var found;
+                function hasMatchingURL(frame) 
+                {
+                    found = (frame.url === url) ? frame : null;
+                    return found;
+                }
+                WebInspector.resourceTreeModel.frames().some(hasMatchingURL);
+                return found;
+            }
+
+            var frame = options.frameURL ? resolveURLToFrame(options.frameURL) : WebInspector.resourceTreeModel.mainFrame;
+            if (!frame) {
+                if (options.frameURL)
+                    console.warn("evaluate: there is no frame with URL " + options.frameURL);
+                else
+                    console.warn("evaluate: the main frame is not yet available");
+                return this._status.E_NOTFOUND(options.frameURL || "<top>");
+            }
+
+            var contextSecurityOrigin;
+            if (options.useContentScriptContext)
+                contextSecurityOrigin = securityOrigin;
+            else if (options.scriptExecutionContext)
+                contextSecurityOrigin = options.scriptExecutionContext;
+
+            var frameContextList = WebInspector.runtimeModel.contextListByFrame(frame);
+            var context; 
+            if (contextSecurityOrigin) {
+                context = frameContextList.contextBySecurityOrigin(contextSecurityOrigin);
+                if (!context) {
+                    console.warn("The JS context " + contextSecurityOrigin + " was not found in the frame " + frame.url)
+                    return this._status.E_NOTFOUND(contextSecurityOrigin)
+                }
+            } else {
+                context = frameContextList.mainWorldContext();
+                if (!context) 
+                    return this._status.E_FAILED(frame.url + " has no execution context");
+            }
+
+            contextId = context.id;
+        }
+        RuntimeAgent.evaluate(expression, "extension", exposeCommandLineAPI, true, contextId, returnByValue, false, callback);
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.ExtensionStatus = function()
+{
+    function makeStatus(code, description)
+    {
+        var details = Array.prototype.slice.call(arguments, 2);
+        var status = { code: code, description: description, details: details };
+        if (code !== "OK") {
+            status.isError = true;
+            console.log("Extension server error: " + String.vsprintf(description, details));
+        }
+        return status;
+    }
+
+    this.OK = makeStatus.bind(null, "OK", "OK");
+    this.E_EXISTS = makeStatus.bind(null, "E_EXISTS", "Object already exists: %s");
+    this.E_BADARG = makeStatus.bind(null, "E_BADARG", "Invalid argument %s: %s");
+    this.E_BADARGTYPE = makeStatus.bind(null, "E_BADARGTYPE", "Invalid type for argument %s: got %s, expected %s");
+    this.E_NOTFOUND = makeStatus.bind(null, "E_NOTFOUND", "Object not found: %s");
+    this.E_NOTSUPPORTED = makeStatus.bind(null, "E_NOTSUPPORTED", "Object does not support requested operation: %s");
+    this.E_PROTOCOLERROR = makeStatus.bind(null, "E_PROTOCOLERROR", "Inspector protocol error: %s");
+    this.E_FAILED = makeStatus.bind(null, "E_FAILED", "Operation failed: %s");
+}
+
+WebInspector.addExtensions = function(extensions)
+{
+    WebInspector.extensionServer._addExtensions(extensions);
+}
+
+WebInspector.extensionAPI = {};
+defineCommonExtensionSymbols(WebInspector.extensionAPI);
+
+WebInspector.extensionServer = new WebInspector.ExtensionServer();
+
+window.addExtension = function(page, name)
+{
+    WebInspector.extensionServer._addExtension({
+        startPage: page,
+        name: name,
+    });
+}
diff --git a/Source/devtools/front_end/ExtensionView.js b/Source/devtools/front_end/ExtensionView.js
new file mode 100644
index 0000000..cc2a19a
--- /dev/null
+++ b/Source/devtools/front_end/ExtensionView.js
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {string} id
+ * @param {string} src
+ * @param {string} className
+ */
+WebInspector.ExtensionView = function(id, src, className)
+{
+    WebInspector.View.call(this);
+    this.element.className = "fill";
+
+    this._id = id;
+    this._iframe = document.createElement("iframe");
+    this._iframe.addEventListener("load", this._onLoad.bind(this), false);
+    this._iframe.src = src;
+    this._iframe.className = className;
+    this.setDefaultFocusedElement(this._iframe);
+
+    this.element.appendChild(this._iframe);
+}
+
+WebInspector.ExtensionView.prototype = {
+    wasShown: function()
+    {
+        if (typeof this._frameIndex === "number")
+            WebInspector.extensionServer.notifyViewShown(this._id, this._frameIndex);
+    },
+
+    willHide: function()
+    {
+        if (typeof this._frameIndex === "number")
+            WebInspector.extensionServer.notifyViewHidden(this._id);
+    },
+
+    _onLoad: function()
+    {
+        var frames = /** @type {Window} */ (window.frames);
+        this._frameIndex = Array.prototype.indexOf.call(frames, this._iframe.contentWindow);
+        if (this.isShowing())
+            WebInspector.extensionServer.notifyViewShown(this._id, this._frameIndex);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {string} id
+ */
+WebInspector.ExtensionNotifierView = function(id)
+{
+    WebInspector.View.call(this);
+
+    this._id = id;
+}
+
+WebInspector.ExtensionNotifierView.prototype = {
+    wasShown: function()
+    {
+        WebInspector.extensionServer.notifyViewShown(this._id);
+    },
+
+    willHide: function()
+    {
+        WebInspector.extensionServer.notifyViewHidden(this._id);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/FileContentView.js b/Source/devtools/front_end/FileContentView.js
new file mode 100644
index 0000000..5166017
--- /dev/null
+++ b/Source/devtools/front_end/FileContentView.js
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.FileSystemModel.File} file
+ */
+WebInspector.FileContentView = function(file)
+{
+    WebInspector.View.call(this);
+
+    this._innerView = /** @type {?WebInspector.View} */ (null);
+    this._file = file;
+    this._content = null;
+}
+
+WebInspector.FileContentView.prototype = {
+    wasShown: function()
+    {
+        if (!this._innerView) {
+            if (this._file.isTextFile)
+                this._innerView = new WebInspector.EmptyView("");
+            else
+                this._innerView = new WebInspector.EmptyView(WebInspector.UIString("Binary File"));
+            this.refresh();
+        }
+
+        this._innerView.show(this.element);
+    },
+
+    /**
+     * @param {number} errorCode
+     * @param {FileSystemAgent.Metadata} metadata
+     */
+    _metadataReceived: function(errorCode, metadata)
+    {
+        if (errorCode || !metadata)
+            return;
+
+        if (this._content) {
+            if (!this._content.updateMetadata(metadata))
+                return;
+            var sourceFrame = /** @type {WebInspector.SourceFrame} */ (this._innerView);
+            this._content.requestContent(sourceFrame.setContent.bind(sourceFrame));
+        } else {
+            this._innerView.detach();
+            this._content = new WebInspector.FileContentView.FileContentProvider(this._file, metadata);
+            this._innerView = new WebInspector.SourceFrame(this._content);
+            this._innerView.show(this.element);
+        }
+    },
+
+    refresh: function()
+    {
+        if (!this._innerView)
+            return;
+
+        if (this._file.isTextFile)
+            this._file.requestMetadata(this._metadataReceived.bind(this));
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ContentProvider}
+ * @param {WebInspector.FileSystemModel.File} file
+ * @param {FileSystemAgent.Metadata} metadata
+ */
+WebInspector.FileContentView.FileContentProvider = function(file, metadata)
+{
+    this._file = file;
+    this._metadata = metadata;
+}
+
+WebInspector.FileContentView.FileContentProvider.prototype = {
+    /**
+     * @return {string}
+     */
+    contentURL: function()
+    {
+        return this._file.url;
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function()
+    {
+        return this._file.resourceType;
+    },
+
+    /**
+     * @param {function(?string, boolean, string)} callback
+     */
+    requestContent: function(callback)
+    {
+        var size = /** @type {number} */ (this._metadata.size);
+        this._file.requestFileContent(true, 0, size, this._charset || "", this._fileContentReceived.bind(this, callback));
+    },
+
+    /**
+     * @param {function(?string, boolean, string)} callback
+     * @param {number} errorCode
+     * @param {string=} content
+     * @param {boolean=} base64Encoded
+     * @param {string=} charset
+     */
+    _fileContentReceived: function(callback, errorCode, content, base64Encoded, charset)
+    {
+        if (errorCode || !content) {
+            callback(null, false, "");
+            return;
+        }
+
+        this._charset = charset;
+        callback(content, false, this.contentType().canonicalMimeType());
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback)
+    {
+        setTimeout(callback.bind(null, []), 0);
+    },
+
+    /**
+     * @param {FileSystemAgent.Metadata} metadata
+     * @return {boolean}
+     */
+    updateMetadata: function(metadata)
+    {
+        if (this._metadata.modificationTime >= metadata.modificationTime)
+            return false;
+        this._metadata = metadata.modificationTime;
+        return true;
+    }
+}
diff --git a/Source/devtools/front_end/FileManager.js b/Source/devtools/front_end/FileManager.js
new file mode 100644
index 0000000..4cd2680
--- /dev/null
+++ b/Source/devtools/front_end/FileManager.js
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.FileManager = function()
+{
+}
+
+WebInspector.FileManager.EventTypes = {
+    SavedURL: "SavedURL",
+    AppendedToURL: "AppendedToURL"
+}
+
+WebInspector.FileManager.prototype = {
+    /**
+     * @return {boolean}
+     */
+    canSave: function()
+    {
+        return true;
+    },
+
+    /**
+     * @param {string} url
+     * @param {string} content
+     * @param {boolean} forceSaveAs
+     */
+    save: function(url, content, forceSaveAs)
+    {
+        // Remove this url from the saved URLs while it is being saved.
+        var savedURLs = WebInspector.settings.savedURLs.get();
+        delete savedURLs[url];
+        WebInspector.settings.savedURLs.set(savedURLs);
+        InspectorFrontendHost.save(url, content, forceSaveAs);
+    },
+
+    /**
+     * @param {string} url
+     */
+    savedURL: function(url)
+    {
+        var savedURLs = WebInspector.settings.savedURLs.get();
+        savedURLs[url] = true;
+        WebInspector.settings.savedURLs.set(savedURLs);
+        this.dispatchEventToListeners(WebInspector.FileManager.EventTypes.SavedURL, url);
+    },
+
+    /**
+     * @param {string} url
+     * @return {boolean}
+     */
+    isURLSaved: function(url)
+    {
+        var savedURLs = WebInspector.settings.savedURLs.get();
+        return savedURLs[url];
+    },
+
+    /**
+     * @param {string} url
+     * @param {string} content
+     */
+    append: function(url, content)
+    {
+        InspectorFrontendHost.append(url, content);
+    },
+
+    /**
+     * @param {string} url
+     */
+    close: function(url)
+    {
+        InspectorFrontendHost.close(url);
+    },
+
+    /**
+     * @param {string} url
+     */
+    appendedToURL: function(url)
+    {
+        this.dispatchEventToListeners(WebInspector.FileManager.EventTypes.AppendedToURL, url);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+WebInspector.fileManager = new WebInspector.FileManager();
diff --git a/Source/devtools/front_end/FileMapping.js b/Source/devtools/front_end/FileMapping.js
new file mode 100644
index 0000000..055db59
--- /dev/null
+++ b/Source/devtools/front_end/FileMapping.js
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.FileMapping = function()
+{
+    this._mappingEntriesSetting = WebInspector.settings.createSetting("fileMappingEntries", []);
+    /** @type {Array.<WebInspector.FileMapping.Entry>} */
+    this._entries = [];
+    this._loadFromSettings();
+}
+
+WebInspector.FileMapping.prototype = {
+    /**
+     * @param {string} url
+     * @return {?WebInspector.FileMapping.Entry}
+     */
+    mappingEntryForURL: function(url)
+    {
+        for (var i = 0; i < this._entries.length; ++i) {
+            var entry = this._entries[i];
+            if (url.startsWith(entry.urlPrefix))
+                return entry;
+        }
+        return null;
+    },
+
+    /**
+     * @param {string} path
+     * @return {?WebInspector.FileMapping.Entry}
+     */
+    mappingEntryForPath: function(path)
+    {
+        for (var i = 0; i < this._entries.length; ++i) {
+            var entry = this._entries[i];
+            if (path.startsWith(entry.pathPrefix))
+                return entry;
+        }
+        return null;
+    },
+
+    /**
+     * @return {Array.<WebInspector.FileMapping.Entry>}
+     */
+    mappingEntries: function()
+    {
+        return this._entries.slice();
+    },
+
+    /**
+     * @param {Array.<WebInspector.FileMapping.Entry>} mappingEntries
+     */
+    setMappingEntries: function(mappingEntries)
+    {
+        this._entries = mappingEntries;
+        this._mappingEntriesSetting.set(mappingEntries);
+    },
+
+    _loadFromSettings: function()
+    {
+        var savedEntries = this._mappingEntriesSetting.get();
+        this._entries = [];
+        for (var i = 0; i < savedEntries.length; ++i) {
+            var entry = new WebInspector.FileMapping.Entry(savedEntries[i].urlPrefix, savedEntries[i].pathPrefix);
+            this._entries.push(entry);
+        }
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {string} urlPrefix
+ * @param {string} pathPrefix
+ */
+WebInspector.FileMapping.Entry = function(urlPrefix, pathPrefix)
+{
+    this.urlPrefix = urlPrefix;
+    this.pathPrefix = pathPrefix;
+}
+
+/**
+ * @type {?WebInspector.FileMapping}
+ */
+WebInspector.fileMapping = null;
diff --git a/Source/devtools/front_end/FileSystemMapping.js b/Source/devtools/front_end/FileSystemMapping.js
new file mode 100644
index 0000000..b06cec2
--- /dev/null
+++ b/Source/devtools/front_end/FileSystemMapping.js
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.FileSystemMapping = function() { }
+
+WebInspector.FileSystemMapping.prototype = {
+    /**
+     * @return {Array.<string>}
+     */
+    fileSystemPaths: function() { },
+
+    /**
+     * @param {string} prefix
+     * @return {?string}
+     */
+    fileSystemPathForPrefix: function(prefix) { }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.FileSystemMapping}
+ * @extends {WebInspector.Object}
+ */
+WebInspector.FileSystemMappingImpl = function()
+{
+    WebInspector.Object.call(this);
+    this._fileSystemMappingSetting = WebInspector.settings.createSetting("fileSystemMapping", {});
+    /** @type {!Object.<string, boolean>} */
+    this._fileSystemPaths = {};
+    this._loadFromSettings();
+}
+
+WebInspector.FileSystemMappingImpl.prototype = {
+    _loadFromSettings: function()
+    {
+        var savedMapping = this._fileSystemMappingSetting.get();
+        this._fileSystemPaths = savedMapping ? /** @type {!Object.<string, string>} */ (savedMapping.registeredFileSystemPaths) || {} : {};
+    },
+
+    _saveToSettings: function()
+    {
+        var savedMapping = {};
+        savedMapping.registeredFileSystemPaths = this._fileSystemPaths;
+        this._fileSystemMappingSetting.set(savedMapping);
+    },
+
+
+    /**
+     * @param {string} fileSystemPath
+     */
+    addFileSystemMapping: function(fileSystemPath)
+    {
+        if (this._fileSystemPaths[fileSystemPath])
+            return;
+
+        this._fileSystemPaths[fileSystemPath] = true;
+        this._saveToSettings();
+        delete this._cachedFileSystemPaths;
+    },
+
+    /**
+     * @param {string} fileSystemPath
+     */
+    removeFileSystemMapping: function(fileSystemPath)
+    {
+        if (!this._fileSystemPaths[fileSystemPath])
+            return;
+        delete this._fileSystemPaths[fileSystemPath];
+        this._saveToSettings();
+        delete this._cachedFileSystemPaths;
+    },
+
+    /**
+     * @return {Array.<string>}
+     */
+    fileSystemPaths: function()
+    {
+        return Object.keys(this._fileSystemPaths);
+    },
+
+    /**
+     * @param {string} prefix
+     * @return {?string}
+     */
+    fileSystemPathForPrefix: function(prefix)
+    {
+        this._cachedFileSystemPaths = this._cachedFileSystemPaths || {};
+        if (this._cachedFileSystemPaths.hasOwnProperty(prefix))
+            return this._cachedFileSystemPaths[prefix];
+        var result = null;
+        for (var fileSystemPath in this._fileSystemPaths) {
+            if (prefix.startsWith(fileSystemPath + "/")) {
+                result = fileSystemPath;
+                break;
+            }
+        }
+        this._cachedFileSystemPaths[prefix] = result;
+        return result;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
diff --git a/Source/devtools/front_end/FileSystemModel.js b/Source/devtools/front_end/FileSystemModel.js
new file mode 100644
index 0000000..14c1372
--- /dev/null
+++ b/Source/devtools/front_end/FileSystemModel.js
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.FileSystemModel = function()
+{
+    WebInspector.Object.call(this);
+
+    this._fileSystemsForOrigin = {};
+
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginAdded, this._securityOriginAdded, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginRemoved, this._securityOriginRemoved, this);
+
+    FileSystemAgent.enable();
+
+    this._reset();
+}
+
+WebInspector.FileSystemModel.prototype = {
+    _reset: function()
+    {
+        for (var securityOrigin in this._fileSystemsForOrigin)
+            this._removeOrigin(securityOrigin);
+        var securityOrigins = WebInspector.resourceTreeModel.securityOrigins();
+        for (var i = 0; i < securityOrigins.length; ++i)
+            this._addOrigin(securityOrigins[i]);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _securityOriginAdded: function(event)
+    {
+        var securityOrigin = /** @type {string} */ (event.data);
+        this._addOrigin(securityOrigin);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _securityOriginRemoved: function(event)
+    {
+        var securityOrigin = /** @type {string} */ (event.data);
+        this._removeOrigin(securityOrigin);
+    },
+
+    /**
+     * @param {string} securityOrigin
+     */
+    _addOrigin: function(securityOrigin)
+    {
+        this._fileSystemsForOrigin[securityOrigin] = {};
+
+        var types = ["persistent", "temporary"];
+        for (var i = 0; i < types.length; ++i)
+            this._requestFileSystemRoot(securityOrigin, types[i], this._fileSystemRootReceived.bind(this, securityOrigin, types[i], this._fileSystemsForOrigin[securityOrigin]));
+    },
+
+    /**
+     * @param {string} securityOrigin
+     */
+    _removeOrigin: function(securityOrigin)
+    {
+        for (var type in this._fileSystemsForOrigin[securityOrigin]) {
+            var fileSystem = this._fileSystemsForOrigin[securityOrigin][type];
+            delete this._fileSystemsForOrigin[securityOrigin][type];
+            this._fileSystemRemoved(fileSystem);
+        }
+        delete this._fileSystemsForOrigin[securityOrigin];
+    },
+
+    /**
+     * @param {string} origin
+     * @param {string} type
+     * @param {function(number, FileSystemAgent.Entry=)} callback
+     */
+    _requestFileSystemRoot: function(origin, type, callback)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {number} errorCode
+         * @param {FileSystemAgent.Entry=} backendRootEntry
+         */
+        function innerCallback(error, errorCode, backendRootEntry)
+        {
+            if (error) {
+                callback(FileError.SECURITY_ERR);
+                return;
+            }
+            
+            callback(errorCode, backendRootEntry);
+        }
+
+        FileSystemAgent.requestFileSystemRoot(origin, type, innerCallback.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.FileSystemModel.FileSystem} fileSystem
+     */
+    _fileSystemAdded: function(fileSystem)
+    {
+        this.dispatchEventToListeners(WebInspector.FileSystemModel.EventTypes.FileSystemAdded, fileSystem);
+    },
+
+    /**
+     * @param {WebInspector.FileSystemModel.FileSystem} fileSystem
+     */
+    _fileSystemRemoved: function(fileSystem)
+    {
+        this.dispatchEventToListeners(WebInspector.FileSystemModel.EventTypes.FileSystemRemoved, fileSystem);
+    },
+
+    refreshFileSystemList: function()
+    {
+        this._reset();
+    },
+
+    /**
+     * @param {string} origin
+     * @param {string} type
+     * @param {Object.<WebInspector.FileSystemModel.FileSystem>} store
+     * @param {number} errorCode
+     * @param {FileSystemAgent.Entry=} backendRootEntry
+     */
+    _fileSystemRootReceived: function(origin, type, store, errorCode, backendRootEntry)
+    {
+        if (!errorCode && backendRootEntry && this._fileSystemsForOrigin[origin] === store) {
+            var fileSystem = new WebInspector.FileSystemModel.FileSystem(this, origin, type, backendRootEntry);
+            store[type] = fileSystem;
+            this._fileSystemAdded(fileSystem);
+        }
+    },
+
+    /**
+     * @param {WebInspector.FileSystemModel.Directory} directory
+     * @param {function(number, Array.<WebInspector.FileSystemModel.Entry>=)} callback
+     */
+    requestDirectoryContent: function(directory, callback)
+    {
+        this._requestDirectoryContent(directory.url, this._directoryContentReceived.bind(this, directory, callback));
+    },
+
+    /**
+     * @param {string} url
+     * @param {function(number, Array.<FileSystemAgent.Entry>=)} callback
+     */
+    _requestDirectoryContent: function(url, callback)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {number} errorCode
+         * @param {Array.<FileSystemAgent.Entry>=} backendEntries
+         */
+        function innerCallback(error, errorCode, backendEntries)
+        {
+            if (error) {
+                callback(FileError.SECURITY_ERR);
+                return;
+            }
+            
+            if (errorCode !== 0) {
+                callback(errorCode, null);
+                return;
+            }
+
+            callback(errorCode, backendEntries);
+        }
+
+        FileSystemAgent.requestDirectoryContent(url, innerCallback.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.FileSystemModel.Directory} parentDirectory
+     * @param {function(number, Array.<WebInspector.FileSystemModel.Entry>=)} callback
+     * @param {number} errorCode
+     * @param {Array.<FileSystemAgent.Entry>=} backendEntries
+     */
+    _directoryContentReceived: function(parentDirectory, callback, errorCode, backendEntries)
+    {
+        var entries = [];
+        for (var i = 0; i < backendEntries.length; ++i) {
+            if (backendEntries[i].isDirectory)
+                entries.push(new WebInspector.FileSystemModel.Directory(this, parentDirectory.fileSystem, backendEntries[i]));
+            else
+                entries.push(new WebInspector.FileSystemModel.File(this, parentDirectory.fileSystem, backendEntries[i]));
+        }
+
+        callback(errorCode, entries);
+    },
+
+    /**
+     * @param {WebInspector.FileSystemModel.Entry} entry
+     * @param {function(number, FileSystemAgent.Metadata=)} callback
+     */
+    requestMetadata: function(entry, callback)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {number} errorCode
+         * @param {FileSystemAgent.Metadata=} metadata
+         */
+        function innerCallback(error, errorCode, metadata)
+        {
+            if (error) {
+                callback(FileError.SECURITY_ERR);
+                return;
+            }
+            
+            callback(errorCode, metadata);
+        }
+
+        FileSystemAgent.requestMetadata(entry.url, innerCallback.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.FileSystemModel.File} file
+     * @param {boolean} readAsText
+     * @param {number=} start
+     * @param {number=} end
+     * @param {string=} charset
+     * @param {function(number, string=, string=)=} callback
+     */
+    requestFileContent: function(file, readAsText, start, end, charset, callback)
+    {
+        this._requestFileContent(file.url, readAsText, start, end, charset, callback);
+    },
+
+    /**
+     * @param {string} url
+     * @param {boolean} readAsText
+     * @param {number=} start
+     * @param {number=} end
+     * @param {string=} charset
+     * @param {function(number, string=, string=)=} callback
+     */
+    _requestFileContent: function(url, readAsText, start, end, charset, callback)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {number} errorCode
+         * @param {string=} content
+         * @param {string=} charset
+         */
+        function innerCallback(error, errorCode, content, charset)
+        {
+            if (error) {
+                if (callback)
+                    callback(FileError.SECURITY_ERR);
+                return;
+            }
+            
+            if (callback)
+                callback(errorCode, content, charset);
+        }
+
+        FileSystemAgent.requestFileContent(url, readAsText, start, end, charset, innerCallback.bind(this));
+    },
+    /**
+     * @param {WebInspector.FileSystemModel.Entry} entry
+     * @param {function(number)=} callback
+     */
+    deleteEntry: function(entry, callback)
+    {
+        var fileSystemModel = this;
+        if (entry === entry.fileSystem.root)
+            this._deleteEntry(entry.url, hookFileSystemDeletion);
+        else
+            this._deleteEntry(entry.url, callback);
+
+        function hookFileSystemDeletion(errorCode)
+        {
+            callback(errorCode);
+            if (!errorCode)
+                fileSystemModel._removeFileSystem(entry.fileSystem);
+        }
+    },
+
+    /**
+     * @param {string} url
+     * @param {function(number)=} callback
+     */
+    _deleteEntry: function(url, callback)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {number} errorCode
+         */
+        function innerCallback(error, errorCode)
+        {
+            if (error) {
+                if (callback)
+                    callback(FileError.SECURITY_ERR);
+                return;
+            }
+            
+            if (callback)
+                callback(errorCode);
+        }
+
+        FileSystemAgent.deleteEntry(url, innerCallback.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.FileSystemModel.FileSystem} fileSystem
+     */
+    _removeFileSystem: function(fileSystem)
+    {
+        var origin = fileSystem.origin;
+        var type = fileSystem.type;
+        if (this._fileSystemsForOrigin[origin] && this._fileSystemsForOrigin[origin][type]) {
+            delete this._fileSystemsForOrigin[origin][type];
+            this._fileSystemRemoved(fileSystem);
+
+            if (Object.isEmpty(this._fileSystemsForOrigin[origin]))
+                delete this._fileSystemsForOrigin[origin];
+        }
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+
+WebInspector.FileSystemModel.EventTypes = {
+    FileSystemAdded: "FileSystemAdded",
+    FileSystemRemoved: "FileSystemRemoved"
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.FileSystemModel} fileSystemModel
+ * @param {string} origin
+ * @param {string} type
+ * @param {FileSystemAgent.Entry} backendRootEntry
+ */
+WebInspector.FileSystemModel.FileSystem = function(fileSystemModel, origin, type, backendRootEntry)
+{
+    this.origin = origin;
+    this.type = type;
+
+    this.root = new WebInspector.FileSystemModel.Directory(fileSystemModel, this, backendRootEntry);
+}
+
+WebInspector.FileSystemModel.FileSystem.prototype = {
+    /**
+     * @type {string}
+     */
+    get name()
+    {
+        return "filesystem:" + this.origin + "/" + this.type;
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.FileSystemModel} fileSystemModel
+ * @param {WebInspector.FileSystemModel.FileSystem} fileSystem
+ * @param {FileSystemAgent.Entry} backendEntry
+ */
+WebInspector.FileSystemModel.Entry = function(fileSystemModel, fileSystem, backendEntry)
+{
+    this._fileSystemModel = fileSystemModel;
+    this._fileSystem = fileSystem;
+
+    this._url = backendEntry.url;
+    this._name = backendEntry.name;
+    this._isDirectory = backendEntry.isDirectory;
+}
+
+/**
+ * @param {WebInspector.FileSystemModel.Entry} x
+ * @param {WebInspector.FileSystemModel.Entry} y
+ * @return {number}
+ */
+WebInspector.FileSystemModel.Entry.compare = function(x, y)
+{
+    if (x.isDirectory != y.isDirectory)
+        return y.isDirectory ? 1 : -1;
+    return x.name.compareTo(y.name);
+}
+
+WebInspector.FileSystemModel.Entry.prototype = {
+    /**
+     * @type {WebInspector.FileSystemModel}
+     */
+    get fileSystemModel()
+    {
+        return this._fileSystemModel;
+    },
+
+    /**
+     * @type {WebInspector.FileSystemModel.FileSystem}
+     */
+    get fileSystem()
+    {
+        return this._fileSystem;
+    },
+
+    /**
+     * @type {string}
+     */
+    get url()
+    {
+        return this._url;
+    },
+
+    /**
+     * @type {string}
+     */
+    get name()
+    {
+        return this._name;
+    },
+
+    /**
+     * @type {boolean}
+     */
+    get isDirectory()
+    {
+        return this._isDirectory;
+    },
+
+    /**
+     * @param {function(number, FileSystemAgent.Metadata)} callback
+     */
+    requestMetadata: function(callback)
+    {
+        this.fileSystemModel.requestMetadata(this, callback);
+    },
+
+    /**
+     * @param {function(number)} callback
+     */
+    deleteEntry: function(callback)
+    {
+        this.fileSystemModel.deleteEntry(this, callback);
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.FileSystemModel.Entry}
+ * @param {WebInspector.FileSystemModel} fileSystemModel
+ * @param {WebInspector.FileSystemModel.FileSystem} fileSystem
+ * @param {FileSystemAgent.Entry} backendEntry
+ */
+WebInspector.FileSystemModel.Directory = function(fileSystemModel, fileSystem, backendEntry)
+{
+    WebInspector.FileSystemModel.Entry.call(this, fileSystemModel, fileSystem, backendEntry);
+}
+
+WebInspector.FileSystemModel.Directory.prototype = {
+    /**
+     * @param {function(number, Array.<WebInspector.FileSystemModel.Directory>)} callback
+     */
+    requestDirectoryContent: function(callback)
+    {
+        this.fileSystemModel.requestDirectoryContent(this, callback);
+    },
+
+    __proto__: WebInspector.FileSystemModel.Entry.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.FileSystemModel.Entry}
+ * @param {WebInspector.FileSystemModel} fileSystemModel
+ * @param {WebInspector.FileSystemModel.FileSystem} fileSystem
+ * @param {FileSystemAgent.Entry} backendEntry
+ */
+WebInspector.FileSystemModel.File = function(fileSystemModel, fileSystem, backendEntry)
+{
+    WebInspector.FileSystemModel.Entry.call(this, fileSystemModel, fileSystem, backendEntry);
+
+    this._mimeType = backendEntry.mimeType;
+    this._resourceType = WebInspector.resourceTypes[backendEntry.resourceType];
+    this._isTextFile = backendEntry.isTextFile;
+}
+
+WebInspector.FileSystemModel.File.prototype = {
+    /**
+     * @type {string}
+     */
+    get mimeType()
+    {
+        return this._mimeType;
+    },
+
+    /**
+     * @type {WebInspector.ResourceType}
+     */
+    get resourceType()
+    {
+        return this._resourceType;
+    },
+
+    /**
+     * @type {boolean}
+     */
+    get isTextFile()
+    {
+        return this._isTextFile;
+    },
+
+    /**
+     * @param {boolean} readAsText
+     * @param {number=} start
+     * @param {number=} end
+     * @param {string=} charset
+     * @param {function(number, string=)=} callback
+     */
+    requestFileContent: function(readAsText, start, end, charset, callback)
+    {
+        this.fileSystemModel.requestFileContent(this, readAsText, start, end, charset, callback);
+    },
+
+    __proto__: WebInspector.FileSystemModel.Entry.prototype
+}
diff --git a/Source/devtools/front_end/FileSystemProjectDelegate.js b/Source/devtools/front_end/FileSystemProjectDelegate.js
new file mode 100644
index 0000000..9c2cc28
--- /dev/null
+++ b/Source/devtools/front_end/FileSystemProjectDelegate.js
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.ProjectDelegate}
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.IsolatedFileSystem} isolatedFileSystem
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.FileSystemProjectDelegate = function(isolatedFileSystem, workspace)
+{
+    this._fileSystem = isolatedFileSystem;
+    this._workspace = workspace;
+}
+
+WebInspector.FileSystemProjectDelegate._scriptExtensions = ["js", "java", "cc", "cpp", "h", "cs", "py", "php"].keySet();
+WebInspector.FileSystemProjectDelegate._styleSheetExtensions = ["css", "scss", "sass"].keySet();
+
+WebInspector.FileSystemProjectDelegate.projectId = function(fileSystemPath)
+{
+    return "filesystem:" + fileSystemPath;
+}
+
+WebInspector.FileSystemProjectDelegate.prototype = {
+    /**
+     * @return {string}
+     */
+    id: function()
+    {
+        return WebInspector.FileSystemProjectDelegate.projectId(this._fileSystem.path());
+    },
+
+    /**
+     * @return {string}
+     */
+    type: function()
+    {
+        return WebInspector.projectTypes.FileSystem;
+    },
+
+    /**
+     * @return {string}
+     */
+    fileSystemPath: function()
+    {
+        return this._fileSystem.path();
+    },
+
+    /**
+     * @return {string}
+     */
+    displayName: function()
+    {
+        return this._fileSystem.path().substr(this._fileSystem.path().lastIndexOf("/") + 1);
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @return {string}
+     */
+    _filePathForPath: function(path)
+    {
+        return "/" + path.join("/");
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestFileContent: function(path, callback)
+    {
+        var filePath = this._filePathForPath(path);
+        this._fileSystem.requestFileContent(filePath, innerCallback.bind(this));
+        
+        /**
+         * @param {?string} content
+         */
+        function innerCallback(content)
+        {
+            var contentType = this._contentTypeForPath(path);
+            callback(content, false, contentType.canonicalMimeType());
+        }
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canSetFileContent: function()
+    {
+        return true;
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {string} newContent
+     * @param {function(?string)} callback
+     */
+    setFileContent: function(path, newContent, callback)
+    {
+        var filePath = this._filePathForPath(path);
+        this._fileSystem.setFileContent(filePath, newContent, callback.bind(this, ""));
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInFileContent: function(path, query, caseSensitive, isRegex, callback)
+    {
+        var filePath = this._filePathForPath(path);
+        this._fileSystem.requestFileContent(filePath, contentCallback.bind(this));
+
+        /**
+         * @param {?string} content
+         */
+        function contentCallback(content)
+        {
+            var result = [];
+            if (content !== null)
+                result = WebInspector.ContentProvider.performSearchInContent(content, query, caseSensitive, isRegex);
+            callback(result);
+        }
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @return {WebInspector.ResourceType}
+     */
+    _contentTypeForPath: function(path)
+    {
+        var fileName = path[path.length - 1];
+        var extensionIndex = fileName.lastIndexOf(".");
+        var extension = "";
+        if (extensionIndex !== -1)
+            extension = fileName.substring(extensionIndex + 1).toLowerCase();
+        var contentType = WebInspector.resourceTypes.Other;
+        if (WebInspector.FileSystemProjectDelegate._scriptExtensions[extension])
+            return WebInspector.resourceTypes.Script;
+        if (WebInspector.FileSystemProjectDelegate._styleSheetExtensions[extension])
+            return WebInspector.resourceTypes.Stylesheet;
+        if (extension === "html" || extension === "htm")
+            return WebInspector.resourceTypes.Document;
+        return WebInspector.resourceTypes.Other;
+    },
+
+    populate: function()
+    {
+        this._fileSystem.requestFilesRecursive("", fileLoaded.bind(this));
+
+        function fileLoaded(filePath)
+        {
+            var path = filePath.split("/");
+            path.shift();
+            console.assert(path.length);
+            var fullPath = this._fileSystem.path() + filePath;
+            var url = this._workspace.urlForPath(fullPath);
+            var contentType = this._contentTypeForPath(path);
+            var fileDescriptor = new WebInspector.FileDescriptor(path, "file://" + fullPath, url, contentType, true);
+            this._addFile(fileDescriptor);
+        }
+    },
+
+    /**
+     * @param {WebInspector.FileDescriptor} fileDescriptor
+     */
+    _addFile: function(fileDescriptor)
+    {
+        this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.FileAdded, fileDescriptor);
+    },
+
+    /**
+     * @param {Array.<string>} path
+     */
+    _removeFile: function(path)
+    {
+        this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.FileRemoved, path);
+    },
+
+    reset: function()
+    {
+        this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.Reset, null);
+    },
+    
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @type {?WebInspector.FileSystemProjectDelegate}
+ */
+WebInspector.fileSystemProjectDelegate = null;
+
+/**
+ * @constructor
+ * @param {WebInspector.IsolatedFileSystemManager} isolatedFileSystemManager
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.FileSystemWorkspaceProvider = function(isolatedFileSystemManager, workspace)
+{
+    this._isolatedFileSystemManager = isolatedFileSystemManager;
+    this._workspace = workspace;
+    this._isolatedFileSystemManager.addEventListener(WebInspector.IsolatedFileSystemManager.Events.FileSystemAdded, this._fileSystemAdded, this);
+    this._isolatedFileSystemManager.addEventListener(WebInspector.IsolatedFileSystemManager.Events.FileSystemRemoved, this._fileSystemRemoved, this);
+    this._simpleProjectDelegates = {};
+}
+
+WebInspector.FileSystemWorkspaceProvider.prototype = {
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _fileSystemAdded: function(event)
+    {
+        var fileSystem = /** @type {WebInspector.IsolatedFileSystem} */ (event.data);
+        var projectId = WebInspector.FileSystemProjectDelegate.projectId(fileSystem.path());
+        var projectDelegate = new WebInspector.FileSystemProjectDelegate(fileSystem, this._workspace)
+        this._simpleProjectDelegates[projectDelegate.id()] = projectDelegate;
+        console.assert(!this._workspace.project(projectDelegate.id()));
+        this._workspace.addProject(projectDelegate);
+        projectDelegate.populate();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _fileSystemRemoved: function(event)
+    {
+        var fileSystem = /** @type {WebInspector.IsolatedFileSystem} */ (event.data);
+        var projectId = WebInspector.FileSystemProjectDelegate.projectId(fileSystem.path());
+        this._workspace.removeProject(projectId);
+        delete this._simpleProjectDelegates[projectId];
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    fileSystemPath: function(uiSourceCode)
+    {
+        var projectDelegate = this._simpleProjectDelegates[uiSourceCode.project().id()];
+        return projectDelegate.fileSystemPath();
+    }
+}
+
+/**
+ * @type {?WebInspector.FileSystemWorkspaceProvider}
+ */
+WebInspector.fileSystemWorkspaceProvider = null;
diff --git a/Source/devtools/front_end/FileSystemView.js b/Source/devtools/front_end/FileSystemView.js
new file mode 100644
index 0000000..7f6a150
--- /dev/null
+++ b/Source/devtools/front_end/FileSystemView.js
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarView}
+ * @param {WebInspector.FileSystemModel.FileSystem} fileSystem
+ */
+WebInspector.FileSystemView = function(fileSystem)
+{
+    WebInspector.SidebarView.call(this, WebInspector.SidebarView.SidebarPosition.Start, "FileSystemViewSidebarWidth");
+    this.element.addStyleClass("file-system-view");
+    this.element.addStyleClass("storage-view");
+
+    var directoryTreeElement = this.element.createChild("ol", "filesystem-directory-tree");
+    this._directoryTree = new TreeOutline(directoryTreeElement);
+    this.sidebarElement.appendChild(directoryTreeElement);
+    this.sidebarElement.addStyleClass("outline-disclosure");
+    this.sidebarElement.addStyleClass("sidebar");
+
+    var rootItem = new WebInspector.FileSystemView.EntryTreeElement(this, fileSystem.root);
+    rootItem.expanded = true;
+    this._directoryTree.appendChild(rootItem);
+    this._visibleView = null;
+
+    this._refreshButton = new WebInspector.StatusBarButton(WebInspector.UIString("Refresh"), "refresh-storage-status-bar-item");
+    this._refreshButton.visible = true;
+    this._refreshButton.addEventListener("click", this._refresh, this);
+
+    this._deleteButton = new WebInspector.StatusBarButton(WebInspector.UIString("Delete"), "delete-storage-status-bar-item");
+    this._deleteButton.visible = true;
+    this._deleteButton.addEventListener("click", this._confirmDelete, this);
+}
+
+WebInspector.FileSystemView.prototype = {
+    /**
+     * @type {Array.<Element>}
+     */
+    get statusBarItems()
+    {
+        return [this._refreshButton.element, this._deleteButton.element];
+    },
+
+    /**
+     * @type {WebInspector.View}
+     */
+    get visibleView()
+    {
+        return this._visibleView;
+    },
+
+    /**
+     * @param {WebInspector.View} view
+     */
+    showView: function(view)
+    {
+        if (this._visibleView === view)
+            return;
+        if (this._visibleView)
+            this._visibleView.detach();
+        this._visibleView = view;
+        view.show(this.mainElement);
+    },
+
+    _refresh: function()
+    {
+        this._directoryTree.children[0].refresh();
+    },
+
+    _confirmDelete: function()
+    {
+        if (confirm(WebInspector.UIString("Are you sure you want to delete the selected entry?")))
+            this._delete();
+    },
+
+    _delete: function()
+    {
+        this._directoryTree.selectedTreeElement.deleteEntry();
+    },
+
+    __proto__: WebInspector.SidebarView.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {WebInspector.FileSystemView} fileSystemView
+ * @param {WebInspector.FileSystemModel.Entry} entry
+ */
+WebInspector.FileSystemView.EntryTreeElement = function(fileSystemView, entry)
+{
+    TreeElement.call(this, entry.name, null, entry.isDirectory);
+
+    this._entry = entry;
+    this._fileSystemView = fileSystemView;
+}
+
+WebInspector.FileSystemView.EntryTreeElement.prototype = {
+    onattach: function()
+    {
+        var selection = this.listItemElement.createChild("div", "selection");
+        this.listItemElement.insertBefore(selection, this.listItemElement.firstChild);
+    },
+
+    onselect: function()
+    {
+        if (!this._view) {
+            if (this._entry.isDirectory)
+                this._view = new WebInspector.DirectoryContentView();
+            else {
+                var file = /** @type {WebInspector.FileSystemModel.File} */ (this._entry);
+                this._view = new WebInspector.FileContentView(file);
+            }
+        }
+        this._fileSystemView.showView(this._view);
+        this.refresh();
+    },
+
+    onpopulate: function()
+    {
+        this.refresh();
+    },
+
+    /**
+     * @param {number} errorCode
+     * @param {Array.<WebInspector.FileSystemModel.Entry>=} entries
+     */
+    _directoryContentReceived: function(errorCode, entries)
+    {
+        if (errorCode === FileError.NOT_FOUND_ERR) {
+            if (this.parent !== this.treeOutline)
+                this.parent.refresh();
+            return;
+        }
+
+        if (errorCode !== 0 || !entries) {
+            console.error("Failed to read directory: " + errorCode);
+            return;
+        }
+
+        entries.sort(WebInspector.FileSystemModel.Entry.compare);
+        if (this._view)
+            this._view.showEntries(entries);
+
+        var oldChildren = this.children.slice(0);
+
+        var newEntryIndex = 0;
+        var oldChildIndex = 0;
+        var currentTreeItem = 0;
+        while (newEntryIndex < entries.length && oldChildIndex < oldChildren.length) {
+            var newEntry = entries[newEntryIndex];
+            var oldChild = oldChildren[oldChildIndex];
+            var order = newEntry.name.compareTo(oldChild._entry.name);
+
+            if (order === 0) {
+                if (oldChild._entry.isDirectory)
+                    oldChild.shouldRefreshChildren = true;
+                else
+                    oldChild.refresh();
+
+                ++newEntryIndex;
+                ++oldChildIndex;
+                ++currentTreeItem;
+                continue;
+            }
+            if (order < 0) {
+                this.insertChild(new WebInspector.FileSystemView.EntryTreeElement(this._fileSystemView, newEntry), currentTreeItem);
+                ++newEntryIndex;
+                ++currentTreeItem;
+                continue;
+            }
+
+            this.removeChildAtIndex(currentTreeItem);
+            ++oldChildIndex;
+        }
+        for (; newEntryIndex < entries.length; ++newEntryIndex)
+            this.appendChild(new WebInspector.FileSystemView.EntryTreeElement(this._fileSystemView, entries[newEntryIndex]));
+
+        for (; oldChildIndex < oldChildren.length; ++oldChildIndex)
+            this.removeChild(oldChildren[oldChildIndex]);
+    },
+
+    refresh: function()
+    {
+        if (!this._entry.isDirectory) {
+            if (this._view && this._view === this._fileSystemView.visibleView) {
+                var fileContentView = /** @type {WebInspector.FileContentView} */ (this._view);
+                fileContentView.refresh();
+            }
+        } else
+            this._entry.requestDirectoryContent(this._directoryContentReceived.bind(this));
+    },
+
+    deleteEntry: function()
+    {
+        this._entry.deleteEntry(this._deletionCompleted.bind(this));
+    },
+
+    _deletionCompleted: function()
+    {
+        if (this._entry != this._entry.fileSystem.root)
+            this.parent.refresh();
+    },
+
+    __proto__: TreeElement.prototype
+}
diff --git a/Source/devtools/front_end/FileUtils.js b/Source/devtools/front_end/FileUtils.js
new file mode 100644
index 0000000..f913dea
--- /dev/null
+++ b/Source/devtools/front_end/FileUtils.js
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.OutputStreamDelegate = function()
+{
+}
+
+WebInspector.OutputStreamDelegate.prototype = {
+    onTransferStarted: function() { },
+
+    onTransferFinished: function() { },
+
+    /**
+     * @param {WebInspector.ChunkedReader} reader
+     */
+    onChunkTransferred: function(reader) { },
+
+    /**
+     * @param {WebInspector.ChunkedReader} reader
+     */
+    onError: function(reader, event) { },
+}
+
+/**
+ * @interface
+ */
+WebInspector.OutputStream = function()
+{
+}
+
+WebInspector.OutputStream.prototype = {
+    /**
+     * @param {string} data
+     * @param {function(WebInspector.OutputStream)=} callback
+     */
+    write: function(data, callback) { },
+
+    close: function() { }
+}
+
+/**
+ * @interface
+ */
+WebInspector.ChunkedReader = function()
+{
+}
+
+WebInspector.ChunkedReader.prototype = {
+    /**
+     * @return {number}
+     */
+    fileSize: function() { },
+
+    /**
+     * @return {number}
+     */
+    loadedSize: function() { },
+
+    /**
+     * @return {string}
+     */
+    fileName: function() { },
+
+    cancel: function() { }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ChunkedReader}
+ * @param {!File} file
+ * @param {number} chunkSize
+ * @param {!WebInspector.OutputStreamDelegate} delegate
+ */
+WebInspector.ChunkedFileReader = function(file, chunkSize, delegate)
+{
+    this._file = file;
+    this._fileSize = file.size;
+    this._loadedSize = 0;
+    this._chunkSize = chunkSize;
+    this._delegate = delegate;
+    this._isCanceled = false;
+}
+
+WebInspector.ChunkedFileReader.prototype = {
+    /**
+     * @param {!WebInspector.OutputStream} output
+     */
+    start: function(output)
+    {
+        this._output = output;
+
+        this._reader = new FileReader();
+        this._reader.onload = this._onChunkLoaded.bind(this);
+        this._reader.onerror = this._delegate.onError.bind(this._delegate, this);
+        this._delegate.onTransferStarted();
+        this._loadChunk();
+    },
+
+    cancel: function()
+    {
+        this._isCanceled = true;
+    },
+
+    /**
+     * @return {number}
+     */
+    loadedSize: function()
+    {
+        return this._loadedSize;
+    },
+
+    /**
+     * @return {number}
+     */
+    fileSize: function()
+    {
+        return this._fileSize;
+    },
+
+    /**
+     * @return {string}
+     */
+    fileName: function()
+    {
+        return this._file.name;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onChunkLoaded: function(event)
+    {
+        if (this._isCanceled)
+            return;
+
+        if (event.target.readyState !== FileReader.DONE)
+            return;
+
+        var data = event.target.result;
+        this._loadedSize += data.length;
+
+        this._output.write(data);
+        if (this._isCanceled)
+            return;
+        this._delegate.onChunkTransferred(this);
+
+        if (this._loadedSize === this._fileSize) {
+            this._file = null;
+            this._reader = null;
+            this._output.close();
+            this._delegate.onTransferFinished();
+            return;
+        }
+
+        this._loadChunk();
+    },
+
+    _loadChunk: function()
+    {
+        var chunkStart = this._loadedSize;
+        var chunkEnd = Math.min(this._fileSize, chunkStart + this._chunkSize)
+        var nextPart = this._file.slice(chunkStart, chunkEnd);
+        this._reader.readAsText(nextPart);
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ChunkedReader}
+ * @param {string} url
+ * @param {!WebInspector.OutputStreamDelegate} delegate
+ */
+WebInspector.ChunkedXHRReader = function(url, delegate)
+{
+    this._url = url;
+    this._delegate = delegate;
+    this._fileSize = 0;
+    this._loadedSize = 0;
+    this._isCanceled = false;
+}
+
+WebInspector.ChunkedXHRReader.prototype = {
+    /**
+     * @param {!WebInspector.OutputStream} output
+     */
+    start: function(output)
+    {
+        this._output = output;
+
+        this._xhr = new XMLHttpRequest();
+        this._xhr.open("GET", this._url, true);
+        this._xhr.onload = this._onLoad.bind(this);
+        this._xhr.onprogress = this._onProgress.bind(this);
+        this._xhr.onerror = this._delegate.onError.bind(this._delegate, this);
+        this._xhr.send(null);
+
+        this._delegate.onTransferStarted();
+    },
+
+    cancel: function()
+    {
+        this._isCanceled = true;
+        this._xhr.abort();
+    },
+
+    /**
+     * @return {number}
+     */
+    loadedSize: function()
+    {
+        return this._loadedSize;
+    },
+
+    /**
+     * @return {number}
+     */
+    fileSize: function()
+    {
+        return this._fileSize;
+    },
+
+    /**
+     * @return {string}
+     */
+    fileName: function()
+    {
+        return this._url;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onProgress: function(event)
+    {
+        if (this._isCanceled)
+            return;
+
+        if (event.lengthComputable)
+            this._fileSize = event.total;
+
+        var data = this._xhr.responseText.substring(this._loadedSize);
+        if (!data.length)
+            return;
+
+        this._loadedSize += data.length;
+        this._output.write(data);
+        if (this._isCanceled)
+            return;
+        this._delegate.onChunkTransferred(this);
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onLoad: function(event)
+    {
+        this._onProgress(event);
+
+        if (this._isCanceled)
+            return;
+
+        this._output.close();
+        this._delegate.onTransferFinished();
+    }
+}
+
+/**
+ * @param {function(!File)} callback
+ * @return {Node}
+ */
+WebInspector.createFileSelectorElement = function(callback) {
+    var fileSelectorElement = document.createElement("input");
+    fileSelectorElement.type = "file";
+    fileSelectorElement.setAttribute("tabindex", -1);
+    fileSelectorElement.style.zIndex = -1;
+    fileSelectorElement.style.position = "absolute";
+    fileSelectorElement.onchange = function(event) {
+        callback(fileSelectorElement.files[0]);
+    };
+    return fileSelectorElement;
+}
+
+/**
+ * @param {string} source
+ * @param {number=} startIndex
+ * @param {number=} lastIndex
+ */
+WebInspector.findBalancedCurlyBrackets = function(source, startIndex, lastIndex) {
+    lastIndex = lastIndex || source.length;
+    startIndex = startIndex || 0;
+    var counter = 0;
+    var inString = false;
+
+    for (var index = startIndex; index < lastIndex; ++index) {
+        var character = source[index];
+        if (inString) {
+            if (character === "\\")
+                ++index;
+            else if (character === "\"")
+                inString = false;
+        } else {
+            if (character === "\"")
+                inString = true;
+            else if (character === "{")
+                ++counter;
+            else if (character === "}") {
+                if (--counter === 0)
+                    return index + 1;
+            }
+        }
+    }
+    return -1;
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.OutputStream}
+ */
+WebInspector.FileOutputStream = function()
+{
+}
+
+WebInspector.FileOutputStream.prototype = {
+    /**
+     * @param {string} fileName
+     * @param {function(WebInspector.FileOutputStream, string=)} callback
+     */
+    open: function(fileName, callback)
+    {
+        this._closed = false;
+        this._writeCallbacks = [];
+        this._fileName = fileName;
+        function callbackWrapper()
+        {
+            WebInspector.fileManager.removeEventListener(WebInspector.FileManager.EventTypes.SavedURL, callbackWrapper, this);
+            WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventTypes.AppendedToURL, this._onAppendDone, this);
+            callback(this);
+        }
+        WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventTypes.SavedURL, callbackWrapper, this);
+        WebInspector.fileManager.save(this._fileName, "", true);
+    },
+
+    /**
+     * @param {string} data
+     * @param {function(WebInspector.OutputStream)=} callback
+     */
+    write: function(data, callback)
+    {
+        this._writeCallbacks.push(callback);
+        WebInspector.fileManager.append(this._fileName, data);
+    },
+
+    close: function()
+    {
+        this._closed = true;
+        if (this._writeCallbacks.length)
+            return;
+        WebInspector.fileManager.removeEventListener(WebInspector.FileManager.EventTypes.AppendedToURL, this._onAppendDone, this);
+        WebInspector.fileManager.close(this._fileName);
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onAppendDone: function(event)
+    {
+        if (event.data !== this._fileName)
+            return;
+        if (!this._writeCallbacks.length) {
+            if (this._closed) {
+                WebInspector.fileManager.removeEventListener(WebInspector.FileManager.EventTypes.AppendedToURL, this._onAppendDone, this);
+                WebInspector.fileManager.close(this._fileName);
+            }
+            return;
+        }
+        var callback = this._writeCallbacks.shift();
+        if (callback)
+            callback(this);
+    }
+}
diff --git a/Source/devtools/front_end/FilteredItemSelectionDialog.js b/Source/devtools/front_end/FilteredItemSelectionDialog.js
new file mode 100644
index 0000000..6479c92
--- /dev/null
+++ b/Source/devtools/front_end/FilteredItemSelectionDialog.js
@@ -0,0 +1,834 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.DialogDelegate}
+ * @implements {WebInspector.ViewportControl.Provider}
+ * @param {WebInspector.SelectionDialogContentProvider} delegate
+ */
+WebInspector.FilteredItemSelectionDialog = function(delegate)
+{
+    WebInspector.DialogDelegate.call(this);
+
+    var xhr = new XMLHttpRequest();
+    xhr.open("GET", "filteredItemSelectionDialog.css", false);
+    xhr.send(null);
+
+    this.element = document.createElement("div");
+    this.element.className = "filtered-item-list-dialog";
+    this.element.addEventListener("keydown", this._onKeyDown.bind(this), false);
+    var styleElement = this.element.createChild("style");
+    styleElement.type = "text/css";
+    styleElement.textContent = xhr.responseText;
+
+    this._promptElement = this.element.createChild("input", "monospace");
+    this._promptElement.addEventListener("input", this._onInput.bind(this), false);
+    this._promptElement.type = "text";
+    this._promptElement.setAttribute("spellcheck", "false");
+
+    this._progressElement = this.element.createChild("div", "progress");
+
+    this._filteredItems = [];
+    this._viewportControl = new WebInspector.ViewportControl(this);
+    this._itemElementsContainer = this._viewportControl.element;
+    this._itemElementsContainer.addStyleClass("container");
+    this._itemElementsContainer.addStyleClass("monospace");
+    this._itemElementsContainer.addEventListener("click", this._onClick.bind(this), false);
+    this.element.appendChild(this._itemElementsContainer);
+
+    this._delegate = delegate;
+    this._delegate.requestItems(this._itemsLoaded.bind(this));
+}
+
+WebInspector.FilteredItemSelectionDialog.prototype = {
+    /**
+     * @param {Element} element
+     * @param {Element} relativeToElement
+     */
+    position: function(element, relativeToElement)
+    {
+        const minWidth = 500;
+        const minHeight = 204;
+        var width = Math.max(relativeToElement.offsetWidth * 2 / 3, minWidth);
+        var height = Math.max(relativeToElement.offsetHeight * 2 / 3, minHeight);
+
+        this.element.style.width = width + "px";
+        this.element.style.height = height + "px";
+
+        const shadowPadding = 20; // shadow + padding
+        element.positionAt(
+            relativeToElement.totalOffsetLeft() + Math.max((relativeToElement.offsetWidth - width - 2 * shadowPadding) / 2, shadowPadding),
+            relativeToElement.totalOffsetTop() + Math.max((relativeToElement.offsetHeight - height - 2 * shadowPadding) / 2, shadowPadding));
+    },
+
+    focus: function()
+    {
+        WebInspector.setCurrentFocusElement(this._promptElement);
+        if (this._filteredItems.length && this._viewportControl.lastVisibleIndex() === -1)
+            this._viewportControl.refresh();
+    },
+
+    willHide: function()
+    {
+        if (this._isHiding)
+            return;
+        this._isHiding = true;
+        this._delegate.dispose();
+        if (this._filterTimer)
+            clearTimeout(this._filterTimer);
+    },
+
+    renderAsTwoRows: function()
+    {
+        this._renderAsTwoRows = true;
+    },
+
+    onEnter: function()
+    {
+        if (!this._delegate.itemsCount())
+            return;
+        this._delegate.selectItem(this._filteredItems[this._selectedIndexInFiltered], this._promptElement.value.trim());
+    },
+
+    /**
+     * @param {number} loadedCount
+     * @param {number} totalCount
+     */
+    _itemsLoaded: function(loadedCount, totalCount)
+    {
+        this._loadedCount = loadedCount;
+        this._totalCount = totalCount;
+
+        if (this._loadTimeout)
+            return;
+        this._loadTimeout = setTimeout(this._updateAfterItemsLoaded.bind(this), 100);
+    },
+
+    _updateAfterItemsLoaded: function()
+    {
+        delete this._loadTimeout;
+        this._filterItems();
+        if (this._loadedCount === this._totalCount)
+            this._progressElement.style.backgroundImage = "";
+        else {
+            const color = "rgb(66, 129, 235)";
+            const percent = ((this._loadedCount / this._totalCount) * 100) + "%";
+            this._progressElement.style.backgroundImage = "-webkit-linear-gradient(left, " + color + ", " + color + " " + percent + ",  transparent " + percent + ")";
+        }
+    },
+
+    /**
+     * @param {number} index
+     * @return {Element}
+     */
+    _createItemElement: function(index)
+    {
+        var itemElement = document.createElement("div");
+        itemElement.className = "filtered-item-list-dialog-item " + (this._renderAsTwoRows ? "two-rows" : "one-row");
+        itemElement._titleElement = itemElement.createChild("span");
+        itemElement._titleElement.textContent = this._delegate.itemTitleAt(index);
+        itemElement._titleSuffixElement = itemElement.createChild("span");
+        itemElement._titleSuffixElement.textContent = this._delegate.itemSuffixAt(index);
+        itemElement._subtitleElement = itemElement.createChild("div", "filtered-item-list-dialog-subtitle");
+        itemElement._subtitleElement.textContent = this._delegate.itemSubtitleAt(index) || "\u200B";
+        itemElement._index = index;
+
+        var key = this._delegate.itemKeyAt(index);
+        var ranges = [];
+        var match;
+        if (this._query) {
+            var regex = this._createSearchRegex(this._query, true);
+            while ((match = regex.exec(key)) !== null && match[0])
+                ranges.push({ offset: match.index, length: regex.lastIndex - match.index });
+            if (ranges.length)
+                WebInspector.highlightRangesWithStyleClass(itemElement, ranges, "highlight");
+        }
+        if (index === this._filteredItems[this._selectedIndexInFiltered])
+            itemElement.addStyleClass("selected");
+
+        return itemElement;
+    },
+
+    /**
+     * @param {?string} query
+     * @param {boolean=} isGlobal
+     */
+    _createSearchRegex: function(query, isGlobal)
+    {
+        const toEscape = String.regexSpecialCharacters();
+        var regexString = "";
+        for (var i = 0; i < query.length; ++i) {
+            var c = query.charAt(i);
+            if (toEscape.indexOf(c) !== -1)
+                c = "\\" + c;
+            if (i)
+                regexString += "[^" + c + "]*";
+            regexString += c;
+        }
+        return new RegExp(regexString, "i" + (isGlobal ? "g" : ""));
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} ignoreCase
+     * @param {boolean} camelCase
+     * @return {RegExp}
+     */
+    _createScoringRegex: function(query, ignoreCase, camelCase)
+    {
+        if (!camelCase || (camelCase && ignoreCase))
+            query = query.toUpperCase();
+        var regexString = "";
+        for (var i = 0; i < query.length; ++i) {
+            var c = query.charAt(i);
+            if (c < "A" || c > "Z")
+               continue;
+            if (regexString)
+               regexString += camelCase ? "[^A-Z]*" : "[^-_ .]*[-_ .]";
+            regexString += c;
+        }
+        if (!camelCase)
+            regexString = "(?:^|[-_ .])" + regexString;
+        return new RegExp(regexString, camelCase ? "" : "i");
+    },
+
+    /**
+     * @param {string} query
+     */
+    setQuery: function(query)
+    {
+        this._promptElement.value = query;
+        this._scheduleFilter();
+    },
+
+    _filterItems: function()
+    {
+        delete this._filterTimer;
+
+        var query = this._delegate.rewriteQuery(this._promptElement.value.trim());
+        this._query = query;
+
+        var ignoreCase = (query === query.toLowerCase());
+
+        var filterRegex = query ? this._createSearchRegex(query) : null;
+        var camelCaseScoringRegex = query ? this._createScoringRegex(query, ignoreCase, true) : null;
+        var underscoreScoringRegex = query ? this._createScoringRegex(query, ignoreCase, false) : null;
+
+        var oldSelectedAbsoluteIndex = this._selectedIndexInFiltered ? this._filteredItems[this._selectedIndexInFiltered] : null;
+        this._filteredItems = [];
+        this._selectedIndexInFiltered = 0;
+
+        var cachedKeys = new Array(this._delegate.itemsCount());
+        var scores = query ? new Array(this._delegate.itemsCount()) : null;
+
+        for (var i = 0; i < this._delegate.itemsCount(); ++i) {
+            var key = this._delegate.itemKeyAt(i);
+            if (filterRegex && !filterRegex.test(key))
+                continue;
+            cachedKeys[i] = key;
+            this._filteredItems.push(i);
+
+            if (!filterRegex)
+                continue;
+
+            var score = 0;
+            if (underscoreScoringRegex.test(key))
+                score += 10;
+            if (camelCaseScoringRegex.test(key))
+                score += ignoreCase ? 10 : 20;
+            for (var j = 0; j < key.length && j < query.length; ++j) {
+                if (key[j] === query[j])
+                    score++;
+                if (key[j].toUpperCase() === query[j].toUpperCase())
+                    score++;
+                else
+                    break;
+            }
+            scores[i] = score;
+        }
+
+        function compareFunction(index1, index2)
+        {
+            if (scores) {
+                var score1 = scores[index1];
+                var score2 = scores[index2];
+                if (score1 > score2)
+                    return -1;
+                if (score1 < score2)
+                    return 1;
+            }
+            var key1 = cachedKeys[index1];
+            var key2 = cachedKeys[index2];
+            return key1.compareTo(key2) || (index2 - index1);
+        }
+
+        const numberOfItemsToSort = 100;
+        if (this._filteredItems.length > numberOfItemsToSort)
+            this._filteredItems.sortRange(compareFunction.bind(this), 0, this._filteredItems.length - 1, numberOfItemsToSort);
+        else
+            this._filteredItems.sort(compareFunction.bind(this));
+
+        for (var i = 0; i < this._filteredItems.length; ++i) {
+            if (this._filteredItems[i] === oldSelectedAbsoluteIndex) {
+                this._selectedIndexInFiltered = i;
+                break;
+            }
+        }
+        this._viewportControl.refresh();
+        if (!query)
+            this._selectedIndexInFiltered = 0;
+        this._updateSelection(this._selectedIndexInFiltered, false);
+    },
+
+    _onInput: function(event)
+    {
+        this._scheduleFilter();
+    },
+
+    _onKeyDown: function(event)
+    {
+        var newSelectedIndex = this._selectedIndexInFiltered;
+
+        switch (event.keyCode) {
+        case WebInspector.KeyboardShortcut.Keys.Down.code:
+            if (++newSelectedIndex >= this._filteredItems.length)
+                newSelectedIndex = this._filteredItems.length - 1;
+            this._updateSelection(newSelectedIndex, true);
+            event.consume(true);
+            break;
+        case WebInspector.KeyboardShortcut.Keys.Up.code:
+            if (--newSelectedIndex < 0)
+                newSelectedIndex = 0;
+            this._updateSelection(newSelectedIndex, false);
+            event.consume(true);
+            break;
+        case WebInspector.KeyboardShortcut.Keys.PageDown.code:
+            newSelectedIndex = Math.min(newSelectedIndex + this._viewportControl.rowsPerViewport(), this._filteredItems.length - 1);
+            this._updateSelection(newSelectedIndex, true);
+            event.consume(true);
+            break;
+        case WebInspector.KeyboardShortcut.Keys.PageUp.code:
+            newSelectedIndex = Math.max(newSelectedIndex - this._viewportControl.rowsPerViewport(), 0);
+            this._updateSelection(newSelectedIndex, false);
+            event.consume(true);
+            break;
+        default:
+        }
+    },
+
+    _scheduleFilter: function()
+    {
+        if (this._filterTimer)
+            return;
+        this._filterTimer = setTimeout(this._filterItems.bind(this), 0);
+    },
+
+    /**
+     * @param {number} index  
+     * @param {boolean} makeLast
+     */
+    _updateSelection: function(index, makeLast)
+    { 
+        var element = this._viewportControl.renderedElementAt(this._selectedIndexInFiltered);
+        if (element)
+            element.removeStyleClass("selected");
+        this._viewportControl.scrollItemIntoView(index, makeLast);
+        this._selectedIndexInFiltered = index;
+        element = this._viewportControl.renderedElementAt(index);
+        if (element)
+            element.addStyleClass("selected");
+    },
+
+    _onClick: function(event)
+    {
+        var itemElement = event.target.enclosingNodeOrSelfWithClass("filtered-item-list-dialog-item");
+        if (!itemElement)
+            return;
+        this._delegate.selectItem(itemElement._index, this._promptElement.value.trim());
+        WebInspector.Dialog.hide();
+    },
+
+    /**
+     * @return {number}
+     */
+    itemCount: function()
+    {
+        return this._filteredItems.length;
+    },
+
+    /**
+     * @param {number} index
+     * @return {Element}
+     */
+    itemElement: function(index)
+    {
+        var delegateIndex = this._filteredItems[index];
+        var element = this._createItemElement(delegateIndex);
+        element._filteredIndex = index;
+        return element;
+    },
+
+    __proto__: WebInspector.DialogDelegate.prototype
+}
+
+/**
+ * @interface
+ */
+WebInspector.SelectionDialogContentProvider = function()
+{
+}
+
+WebInspector.SelectionDialogContentProvider.prototype = {
+    /**
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemTitleAt: function(itemIndex) { },
+
+    /*
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemSuffixAt: function(itemIndex) { },
+
+    /*
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemSubtitleAt: function(itemIndex) { },
+
+    /**
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemKeyAt: function(itemIndex) { },
+
+    /**
+     * @return {number}
+     */
+    itemsCount: function() { },
+
+    /**
+     * @param {function(number, number)} callback
+     */
+    requestItems: function(callback) { },
+
+    /**
+     * @param {number} itemIndex
+     * @param {string} promptValue
+     */
+    selectItem: function(itemIndex, promptValue) { },
+
+    /**
+     * @param {string} query
+     * @return {string}
+     */
+    rewriteQuery: function(query) { },
+
+    dispose: function() { }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.SelectionDialogContentProvider}
+ * @param {WebInspector.View} view
+ * @param {WebInspector.ContentProvider} contentProvider
+ */
+WebInspector.JavaScriptOutlineDialog = function(view, contentProvider)
+{
+    WebInspector.SelectionDialogContentProvider.call(this);
+
+    this._functionItems = [];
+    this._view = view;
+    this._contentProvider = contentProvider;
+}
+
+/**
+ * @param {WebInspector.View} view
+ * @param {WebInspector.ContentProvider} contentProvider
+ */
+WebInspector.JavaScriptOutlineDialog.show = function(view, contentProvider)
+{
+    if (WebInspector.Dialog.currentInstance())
+        return null;
+    var delegate = new WebInspector.JavaScriptOutlineDialog(view, contentProvider);
+    var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(delegate);
+    WebInspector.Dialog.show(view.element, filteredItemSelectionDialog);
+}
+
+WebInspector.JavaScriptOutlineDialog.prototype = {
+    /**
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemTitleAt: function(itemIndex)
+    {
+        var functionItem = this._functionItems[itemIndex];
+        return functionItem.name + (functionItem.arguments ? functionItem.arguments : "");
+    },
+
+    /*
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemSuffixAt: function(itemIndex)
+    {
+        return "";
+    },
+
+    /*
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemSubtitleAt: function(itemIndex)
+    {
+        return ":" + (this._functionItems[itemIndex].line + 1);
+    },
+
+    /**
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemKeyAt: function(itemIndex)
+    {
+        return this._functionItems[itemIndex].name;
+    },
+
+    /**
+     * @return {number}
+     */
+    itemsCount: function()
+    {
+        return this._functionItems.length;
+    },
+
+    /**
+     * @param {function(number, number)} callback
+     */
+    requestItems: function(callback)
+    {
+        /**
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function contentCallback(content, contentEncoded, mimeType)
+        {
+            if (this._outlineWorker)
+                this._outlineWorker.terminate();
+            this._outlineWorker = new Worker("ScriptFormatterWorker.js");
+            this._outlineWorker.onmessage = this._didBuildOutlineChunk.bind(this, callback);
+            const method = "outline";
+            this._outlineWorker.postMessage({ method: method, params: { content: content } });
+        }
+        this._contentProvider.requestContent(contentCallback.bind(this));
+    },
+
+    _didBuildOutlineChunk: function(callback, event)
+    {
+        var data = event.data;
+        var chunk = data["chunk"];
+        for (var i = 0; i < chunk.length; ++i)
+            this._functionItems.push(chunk[i]);
+        callback(data.index, data.total);
+
+        if (data.total === data.index && this._outlineWorker) {
+            this._outlineWorker.terminate();
+            delete this._outlineWorker;
+        }
+    },
+
+    /**
+     * @param {number} itemIndex
+     * @param {string} promptValue
+     */
+    selectItem: function(itemIndex, promptValue)
+    {
+        var lineNumber = this._functionItems[itemIndex].line;
+        if (!isNaN(lineNumber) && lineNumber >= 0)
+            this._view.highlightLine(lineNumber);
+        this._view.focus();
+    },
+
+    /**
+     * @param {string} query
+     * @return {string}
+     */
+    rewriteQuery: function(query)
+    {
+        return query;
+    },
+
+    dispose: function()
+    {
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.SelectionDialogContentProvider}
+ */
+WebInspector.SelectUISourceCodeDialog = function()
+{
+    var projects = WebInspector.workspace.projects().filter(this.filterProject.bind(this));
+    this._uiSourceCodes = [];
+    for (var i = 0; i < projects.length; ++i)
+        this._uiSourceCodes = this._uiSourceCodes.concat(projects[i].uiSourceCodes().filter(this.filterUISourceCode.bind(this)));
+    WebInspector.workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
+}
+
+WebInspector.SelectUISourceCodeDialog.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number=} lineNumber
+     */
+    uiSourceCodeSelected: function(uiSourceCode, lineNumber)
+    {
+        // Overridden by subclasses
+    },
+
+    /**
+     * @param {WebInspector.Project} project
+     */
+    filterProject: function(project)
+    {
+        return true;
+        // Overridden by subclasses
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    filterUISourceCode: function(uiSourceCode)
+    {
+        return uiSourceCode.name();
+    },
+
+    /**
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemTitleAt: function(itemIndex)
+    {
+        return this._uiSourceCodes[itemIndex].name().trimEnd(100);
+    },
+
+    /*
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemSuffixAt: function(itemIndex)
+    {
+        return this._queryLineNumber || "";
+    },
+
+    /*
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemSubtitleAt: function(itemIndex)
+    {
+        var uiSourceCode = this._uiSourceCodes[itemIndex]
+        var projectName = uiSourceCode.project().displayName();
+        var path = uiSourceCode.path().slice();
+        path.pop();
+        path.unshift(projectName);
+        return path.join("/");
+    },
+
+    /**
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemKeyAt: function(itemIndex)
+    {
+        return this._uiSourceCodes[itemIndex].name();
+    },
+
+    /**
+     * @return {number}
+     */
+    itemsCount: function()
+    {
+        return this._uiSourceCodes.length;
+    },
+
+    /**
+     * @param {function(number, number)} callback
+     */
+    requestItems: function(callback)
+    {
+        this._itemsLoaded = callback;
+        this._itemsLoaded(1, 1);
+    },
+
+    /**
+     * @param {number} itemIndex
+     * @param {string} promptValue
+     */
+    selectItem: function(itemIndex, promptValue)
+    {
+        var lineNumberMatch = promptValue.match(/[^:]+\:([\d]*)$/);
+        var lineNumber = lineNumberMatch ? Math.max(parseInt(lineNumberMatch[1], 10) - 1, 0) : undefined;
+        this.uiSourceCodeSelected(this._uiSourceCodes[itemIndex], lineNumber);
+    },
+
+    /**
+     * @param {string} query
+     * @return {string}
+     */
+    rewriteQuery: function(query)
+    {
+        if (!query)
+            return query;
+        query = query.trim();
+        var lineNumberMatch = query.match(/([^:]+)(\:[\d]*)$/);
+        this._queryLineNumber = lineNumberMatch ? lineNumberMatch[2] : "";
+        return lineNumberMatch ? lineNumberMatch[1] : query;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _uiSourceCodeAdded: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        if (!this.filterUISourceCode(uiSourceCode))
+            return;
+        this._uiSourceCodes.push(uiSourceCode)
+        this._itemsLoaded(1, 1);
+    },
+
+    dispose: function()
+    {
+        WebInspector.workspace.removeEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SelectUISourceCodeDialog}
+ * @param {WebInspector.ScriptsPanel} panel
+ */
+WebInspector.OpenResourceDialog = function(panel)
+{
+    WebInspector.SelectUISourceCodeDialog.call(this);
+    this._panel = panel;
+}
+
+WebInspector.OpenResourceDialog.prototype = {
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number=} lineNumber
+     */
+    uiSourceCodeSelected: function(uiSourceCode, lineNumber)
+    {
+        this._panel.showUISourceCode(uiSourceCode, lineNumber);
+    },
+
+    /**
+     * @param {WebInspector.Project} project
+     */
+    filterProject: function(project)
+    {
+        return !project.isServiceProject();
+    },
+
+    __proto__: WebInspector.SelectUISourceCodeDialog.prototype
+}
+
+/**
+ * @param {WebInspector.ScriptsPanel} panel
+ * @param {Element} relativeToElement
+ * @param {string=} name
+ */
+WebInspector.OpenResourceDialog.show = function(panel, relativeToElement, name)
+{
+    if (WebInspector.Dialog.currentInstance())
+        return;
+
+    var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.OpenResourceDialog(panel));
+    filteredItemSelectionDialog.renderAsTwoRows();
+    if (name)
+        filteredItemSelectionDialog.setQuery(name);
+    WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog);
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SelectUISourceCodeDialog}
+ * @param {string} type
+ * @param {function(WebInspector.UISourceCode)} callback
+ */
+WebInspector.SelectUISourceCodeForProjectTypeDialog = function(type, callback)
+{
+    this._type = type;
+    WebInspector.SelectUISourceCodeDialog.call(this);
+    this._callback = callback;
+}
+
+WebInspector.SelectUISourceCodeForProjectTypeDialog.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number=} lineNumber
+     */
+    uiSourceCodeSelected: function(uiSourceCode, lineNumber)
+    {
+        this._callback(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.Project} project
+     */
+    filterProject: function(project)
+    {
+        return project.type() === this._type;
+    },
+
+    __proto__: WebInspector.SelectUISourceCodeDialog.prototype
+}
+
+/**
+ * @param {string} type
+ * @param {function(WebInspector.UISourceCode)} callback
+ * @param {Element} relativeToElement
+ */
+WebInspector.SelectUISourceCodeForProjectTypeDialog.show = function(name, type, callback, relativeToElement)
+{
+    if (WebInspector.Dialog.currentInstance())
+        return;
+
+    var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.SelectUISourceCodeForProjectTypeDialog(type, callback));
+    filteredItemSelectionDialog.setQuery(name);
+    filteredItemSelectionDialog.renderAsTwoRows();
+    WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog);
+}
diff --git a/Source/devtools/front_end/FlameChart.js b/Source/devtools/front_end/FlameChart.js
new file mode 100644
index 0000000..6fff037
--- /dev/null
+++ b/Source/devtools/front_end/FlameChart.js
@@ -0,0 +1,747 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.CPUProfileView} cpuProfileView
+ */
+WebInspector.FlameChart = function(cpuProfileView)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("flameChart.css");
+    this.element.className = "fill";
+    this.element.id = "cpu-flame-chart";
+
+    this._overviewContainer = this.element.createChild("div", "overview-container");
+    this._overviewGrid = new WebInspector.OverviewGrid("flame-chart");
+    this._overviewCanvas = this._overviewContainer.createChild("canvas", "flame-chart-overview-canvas");
+    this._overviewContainer.appendChild(this._overviewGrid.element);
+    this._overviewCalculator = new WebInspector.FlameChart.OverviewCalculator();
+    this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
+
+    this._chartContainer = this.element.createChild("div", "chart-container");
+    this._timelineGrid = new WebInspector.TimelineGrid();
+    this._chartContainer.appendChild(this._timelineGrid.element);
+    this._calculator = new WebInspector.FlameChart.Calculator();
+
+    this._canvas = this._chartContainer.createChild("canvas");
+    this._canvas.addEventListener("mousemove", this._onMouseMove.bind(this));
+    WebInspector.installDragHandle(this._canvas, this._startCanvasDragging.bind(this), this._canvasDragging.bind(this), this._endCanvasDragging.bind(this), "col-resize");
+
+    this._cpuProfileView = cpuProfileView;
+    this._windowLeft = 0.0;
+    this._windowRight = 1.0;
+    this._barHeight = 15;
+    this._minWidth = 1;
+    this._paddingLeft = 15;
+    this._canvas.addEventListener("mousewheel", this._onMouseWheel.bind(this), false);
+    this.element.addEventListener("click", this._onClick.bind(this), false);
+    this._linkifier = new WebInspector.Linkifier();
+    this._highlightedNodeIndex = -1;
+
+    if (!WebInspector.FlameChart._colorGenerator)
+        WebInspector.FlameChart._colorGenerator = new WebInspector.FlameChart.ColorGenerator();
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelineGrid.Calculator}
+ */
+WebInspector.FlameChart.Calculator = function()
+{
+}
+
+WebInspector.FlameChart.Calculator.prototype = {
+    /**
+     * @param {WebInspector.FlameChart} flameChart
+     */
+    _updateBoundaries: function(flameChart)
+    {
+        this._minimumBoundaries = flameChart._windowLeft * flameChart._timelineData.totalTime;
+        this._maximumBoundaries = flameChart._windowRight * flameChart._timelineData.totalTime;
+        this.paddingLeft = flameChart._paddingLeft;
+        this._width = flameChart._canvas.width - this.paddingLeft;
+        this._timeToPixel = this._width / this.boundarySpan();
+    },
+
+    /**
+     * @param {number} time
+     */
+    computePosition: function(time)
+    {
+        return (time - this._minimumBoundaries) * this._timeToPixel + this.paddingLeft;
+    },
+
+    formatTime: function(value)
+    {
+        return Number.secondsToString((value + this._minimumBoundaries) / 1000);
+    },
+
+    maximumBoundary: function()
+    {
+        return this._maximumBoundaries;
+    },
+
+    minimumBoundary: function()
+    {
+        return this._minimumBoundaries;
+    },
+
+    zeroTime: function()
+    {
+        return 0;
+    },
+
+    boundarySpan: function()
+    {
+        return this._maximumBoundaries - this._minimumBoundaries;
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelineGrid.Calculator}
+ */
+WebInspector.FlameChart.OverviewCalculator = function()
+{
+}
+
+WebInspector.FlameChart.OverviewCalculator.prototype = {
+    /**
+     * @param {WebInspector.FlameChart} flameChart
+     */
+    _updateBoundaries: function(flameChart)
+    {
+        this._minimumBoundaries = 0;
+        this._maximumBoundaries = flameChart._timelineData.totalTime;
+        this._xScaleFactor = flameChart._canvas.width / flameChart._timelineData.totalTime;
+    },
+
+    /**
+     * @param {number} time
+     */
+    computePosition: function(time)
+    {
+        return (time - this._minimumBoundaries) * this._xScaleFactor;
+    },
+
+    formatTime: function(value)
+    {
+        return Number.secondsToString((value + this._minimumBoundaries) / 1000);
+    },
+
+    maximumBoundary: function()
+    {
+        return this._maximumBoundaries;
+    },
+
+    minimumBoundary: function()
+    {
+        return this._minimumBoundaries;
+    },
+
+    zeroTime: function()
+    {
+        return this._minimumBoundaries;
+    },
+
+    boundarySpan: function()
+    {
+        return this._maximumBoundaries - this._minimumBoundaries;
+    }
+}
+
+WebInspector.FlameChart.Events = {
+    SelectedNode: "SelectedNode"
+}
+
+/**
+ * @constructor
+ */
+WebInspector.FlameChart.ColorGenerator = function()
+{
+    this._colorPairs = {};
+    this._currentColorIndex = 0;
+}
+
+WebInspector.FlameChart.ColorGenerator.prototype = {
+    /**
+     * @param {!string} id
+     */
+    _colorPairForID: function(id)
+    {
+        var colorPairs = this._colorPairs;
+        var colorPair = colorPairs[id];
+        if (!colorPair) {
+            var currentColorIndex = ++this._currentColorIndex;
+            var hue = (currentColorIndex * 5 + 11 * (currentColorIndex % 2)) % 360;
+            colorPairs[id] = colorPair = {highlighted: "hsla(" + hue + ", 100%, 33%, 0.7)", normal: "hsla(" + hue + ", 100%, 66%, 0.7)"};
+        }
+        return colorPair;
+    }
+}
+
+/**
+ * @constructor
+ * @param {!Object} colorPair
+ * @param {!number} depth
+ * @param {!number} duration
+ * @param {!number} startTime
+ * @param {Object} node
+ */
+WebInspector.FlameChart.Entry = function(colorPair, depth, duration, startTime, node)
+{
+    this.colorPair = colorPair;
+    this.depth = depth;
+    this.duration = duration;
+    this.startTime = startTime;
+    this.node = node;
+    this.selfTime = 0;
+}
+
+WebInspector.FlameChart.prototype = {
+    /**
+     * @param {!number} timeLeft
+     * @param {!number} timeRight
+     */
+    selectRange: function(timeLeft, timeRight)
+    {
+        this._overviewGrid.setWindow(timeLeft / this._totalTime, timeRight / this._totalTime);
+    },
+
+    _onWindowChanged: function(event)
+    {
+        this._scheduleUpdate();
+    },
+
+    _startCanvasDragging: function(event)
+    {
+        if (!this._timelineData)
+            return false;
+        this._isDragging = true;
+        this._dragStartPoint = event.pageX;
+        this._dragStartWindowLeft = this._windowLeft;
+        this._dragStartWindowRight = this._windowRight;
+        return true;
+    },
+
+    _canvasDragging: function(event)
+    {
+        var pixelShift = this._dragStartPoint - event.pageX;
+        var windowShift = pixelShift / this._totalPixels;
+
+        var windowLeft = Math.max(0, this._dragStartWindowLeft + windowShift);
+        if (windowLeft === this._windowLeft)
+            return;
+        windowShift = windowLeft - this._dragStartWindowLeft;
+        
+        var windowRight = Math.min(1, this._dragStartWindowRight + windowShift);
+        if (windowRight === this._windowRight)
+            return;
+        windowShift = windowRight - this._dragStartWindowRight;
+        this._overviewGrid.setWindow(this._dragStartWindowLeft + windowShift, this._dragStartWindowRight + windowShift);
+    },
+
+    _endCanvasDragging: function()
+    {
+        this._isDragging = false;
+    },
+
+    _calculateTimelineData: function()
+    {
+        if (this._cpuProfileView.samples)
+            return this._calculateTimelineDataForSamples();
+
+        if (this._timelineData)
+            return this._timelineData;
+
+        if (!this._cpuProfileView.profileHead)
+            return null;
+
+        var index = 0;
+        var entries = [];
+
+        function appendReversedArray(toArray, fromArray)
+        {
+            for (var i = fromArray.length - 1; i >= 0; --i)
+                toArray.push(fromArray[i]);
+        }
+
+        var stack = [];
+        appendReversedArray(stack, this._cpuProfileView.profileHead.children);
+
+        var levelOffsets = /** @type {Array.<!number>} */ ([0]);
+        var levelExitIndexes = /** @type {Array.<!number>} */ ([0]);
+        var colorGenerator = WebInspector.FlameChart._colorGenerator;
+
+        while (stack.length) {
+            var level = levelOffsets.length - 1;
+            var node = stack.pop();
+            var offset = levelOffsets[level];
+
+            var colorPair = colorGenerator._colorPairForID(node.functionName + ":" + node.url + ":" + node.lineNumber);
+
+            entries.push(new WebInspector.FlameChart.Entry(colorPair, level, node.totalTime, offset, node));
+
+            ++index;
+
+            levelOffsets[level] += node.totalTime;
+            if (node.children.length) {
+                levelExitIndexes.push(stack.length);
+                levelOffsets.push(offset + node.selfTime / 2);
+                appendReversedArray(stack, node.children);
+            }
+
+            while (stack.length === levelExitIndexes[levelExitIndexes.length - 1]) {
+                levelOffsets.pop();
+                levelExitIndexes.pop();
+            }
+        }
+
+        this._timelineData = {
+            entries: entries,
+            totalTime: this._cpuProfileView.profileHead.totalTime,
+        }
+
+        return this._timelineData;
+    },
+
+    _calculateTimelineDataForSamples: function()
+    {
+        if (this._timelineData)
+            return this._timelineData;
+
+        if (!this._cpuProfileView.profileHead)
+            return null;
+
+        var samples = this._cpuProfileView.samples;
+        var idToNode = this._cpuProfileView._idToNode;
+        var samplesCount = samples.length;
+
+        var index = 0;
+        var entries = /** @type {Array.<!WebInspector.FlameChart.Entry>} */ ([]);
+
+        var openIntervals = [];
+        var stackTrace = [];
+        var colorGenerator = WebInspector.FlameChart._colorGenerator;
+        for (var sampleIndex = 0; sampleIndex < samplesCount; sampleIndex++) {
+            var node = idToNode[samples[sampleIndex]];
+            stackTrace.length = 0;
+            while (node) {
+                stackTrace.push(node);
+                node = node.parent;
+            }
+            stackTrace.pop(); // Remove (root) node
+
+            var depth = 0;
+            node = stackTrace.pop();
+            var intervalIndex;
+            while (node && depth < openIntervals.length && node === openIntervals[depth].node) {
+                intervalIndex = openIntervals[depth].index;
+                entries[intervalIndex].duration += 1;
+                node = stackTrace.pop();
+                ++depth;
+            }
+            if (depth < openIntervals.length)
+                openIntervals.length = depth;
+            if (!node) {
+                entries[intervalIndex].selfTime += 1;
+                continue;
+            }
+
+            while (node) {
+                var colorPair = colorGenerator._colorPairForID(node.functionName + ":" + node.url + ":" + node.lineNumber);
+
+                entries.push(new WebInspector.FlameChart.Entry(colorPair, depth, 1, sampleIndex, node));
+                openIntervals.push({node: node, index: index});
+                ++index;
+
+                node = stackTrace.pop();
+                ++depth;
+            }
+            entries[entries.length - 1].selfTime += 1;
+        }
+
+        this._timelineData = {
+            entries: entries,
+            totalTime: samplesCount,
+        };
+
+        return this._timelineData;
+    },
+
+    _onMouseMove: function(event)
+    {
+        if (this._isDragging)
+            return;
+
+        var nodeIndex = this._coordinatesToNodeIndex(event.offsetX, event.offsetY);
+
+        if (this._highlightedNodeIndex === nodeIndex)
+            return;
+
+        this._highlightedNodeIndex = nodeIndex;
+        this._scheduleUpdate();
+    },
+
+    _prepareHighlightedEntryInfo: function()
+    {
+        if (this._isDragging)
+            return null;
+        var entry = this._timelineData.entries[this._highlightedNodeIndex];
+        if (!entry)
+            return null;
+        var node = entry.node;
+        if (!node)
+            return null;
+
+        var entryInfo = [];
+        function pushEntryInfoRow(title, text)
+        {
+            var row = {};
+            row.title = title;
+            row.text = text;
+            entryInfo.push(row);
+        }
+
+        pushEntryInfoRow(WebInspector.UIString("Name"), node.functionName);
+        if (this._cpuProfileView.samples) {
+            pushEntryInfoRow(WebInspector.UIString("Self time"), Number.secondsToString(entry.selfTime / 1000, true));
+            pushEntryInfoRow(WebInspector.UIString("Total time"), Number.secondsToString(entry.duration / 1000, true));
+        }
+        pushEntryInfoRow(WebInspector.UIString("Aggregated self time"), Number.secondsToString(node.selfTime / 1000, true));
+        pushEntryInfoRow(WebInspector.UIString("Aggregated total time"), Number.secondsToString(node.totalTime / 1000, true));
+        return entryInfo;
+    },
+
+    _onClick: function(e)
+    {
+        if (this._highlightedNodeIndex === -1)
+            return;
+        var node = this._timelineData.entries[this._highlightedNodeIndex].node;
+        this.dispatchEventToListeners(WebInspector.FlameChart.Events.SelectedNode, node);
+    },
+
+    _onMouseWheel: function(e)
+    {
+        if (e.wheelDeltaY) {
+            const zoomFactor = 1.1;
+            const mouseWheelZoomSpeed = 1 / 120;
+
+            var zoom = Math.pow(zoomFactor, -e.wheelDeltaY * mouseWheelZoomSpeed);
+            var overviewReference = (this._pixelWindowLeft + e.offsetX - this._paddingLeft) / this._totalPixels;
+            this._overviewGrid.zoom(zoom, overviewReference);
+        } else {
+            var shift = Number.constrain(-1 * this._windowWidth / 4 * e.wheelDeltaX / 120, -this._windowLeft, 1 - this._windowRight);
+            this._overviewGrid.setWindow(this._windowLeft + shift, this._windowRight + shift);
+        }
+    },
+
+    /**
+     * @param {!number} x
+     * @param {!number} y
+     */
+    _coordinatesToNodeIndex: function(x, y)
+    {
+        var timelineData = this._timelineData;
+        if (!timelineData)
+            return -1;
+        var timelineEntries = timelineData.entries;
+        var cursorTime = (x + this._pixelWindowLeft - this._paddingLeft) * this._pixelToTime;
+        var cursorLevel = Math.floor((this._canvas.height / window.devicePixelRatio - y) / this._barHeight);
+
+        for (var i = 0; i < timelineEntries.length; ++i) {
+            if (cursorTime < timelineEntries[i].startTime)
+                return -1;
+            if (cursorTime < (timelineEntries[i].startTime + timelineEntries[i].duration)
+                && cursorLevel === timelineEntries[i].depth)
+                return i;
+        }
+        return -1;
+    },
+
+    onResize: function()
+    {
+        this._updateOverviewCanvas = true;
+        this._scheduleUpdate();
+    },
+
+    _drawOverviewCanvas: function(width, height)
+    {
+        if (!this._timelineData)
+            return;
+
+        var timelineEntries = this._timelineData.entries;
+
+        var drawData = new Uint8Array(width);
+        var scaleFactor = width / this._totalTime;
+        var maxStackDepth = 5; // minimum stack depth for the case when we see no activity.
+
+        for (var nodeIndex = 0; nodeIndex < timelineEntries.length; ++nodeIndex) {
+            var entry = timelineEntries[nodeIndex];
+            var start = Math.floor(entry.startTime * scaleFactor);
+            var finish = Math.floor((entry.startTime + entry.duration) * scaleFactor);
+            for (var x = start; x < finish; ++x) {
+                drawData[x] = Math.max(drawData[x], entry.depth + 1);
+                maxStackDepth = Math.max(maxStackDepth, entry.depth + 1);
+            }
+        }
+
+        var ratio = window.devicePixelRatio;
+        var canvasWidth = width * ratio;
+        var canvasHeight = height * ratio;
+        this._overviewCanvas.width = canvasWidth;
+        this._overviewCanvas.height = canvasHeight;
+        this._overviewCanvas.style.width = width + "px";
+        this._overviewCanvas.style.height = height + "px";
+
+        var context = this._overviewCanvas.getContext("2d");
+
+        var yScaleFactor = canvasHeight / (maxStackDepth * 1.1);
+        context.lineWidth = 1;
+        context.translate(0.5, 0.5);
+        context.strokeStyle = "rgba(20,0,0,0.4)";
+        context.fillStyle = "rgba(214,225,254,0.8)";
+        context.moveTo(-1, canvasHeight - 1);
+        if (drawData)
+          context.lineTo(-1, Math.round(height - drawData[0] * yScaleFactor - 1));
+        var value;
+        for (var x = 0; x < width; ++x) {
+            value = Math.round(canvasHeight - drawData[x] * yScaleFactor - 1);
+            context.lineTo(x * ratio, value);
+        }
+        context.lineTo(canvasWidth + 1, value);
+        context.lineTo(canvasWidth + 1, canvasHeight - 1);
+        context.fill();
+        context.stroke();
+        context.closePath();
+    },
+
+    /**
+     * @param {WebInspector.FlameChart.Entry} entry
+     * @param {AnchorBox} anchorBox
+     */
+    _entryToAnchorBox: function(entry, anchorBox)
+    {
+        anchorBox.x = Math.floor(entry.startTime * this._timeToPixel) - this._pixelWindowLeft + this._paddingLeft;
+        anchorBox.y = this._canvas.height / window.devicePixelRatio - (entry.depth + 1) * this._barHeight;
+        anchorBox.width = Math.floor(entry.duration * this._timeToPixel);
+        anchorBox.height = this._barHeight;
+        if (anchorBox.x < 0) {
+            anchorBox.width += anchorBox.x;
+            anchorBox.x = 0;
+        }
+        anchorBox.width = Number.constrain(anchorBox.width, 0, this._canvas.width - anchorBox.x);
+    },
+
+    /**
+     * @param {!number} height
+     * @param {!number} width
+     */
+    draw: function(width, height)
+    {
+        var timelineData = this._calculateTimelineData();
+        if (!timelineData)
+            return;
+        var timelineEntries = timelineData.entries;
+
+        var ratio = window.devicePixelRatio;
+        var canvasWidth = width * ratio;
+        var canvasHeight = height * ratio;
+        this._canvas.width = canvasWidth;
+        this._canvas.height = canvasHeight;
+        this._canvas.style.width = width + "px";
+        this._canvas.style.height = height + "px";
+
+        var barHeight = this._barHeight;
+
+        var context = this._canvas.getContext("2d");
+        var textPaddingLeft = 2;
+        context.scale(ratio, ratio);
+        context.font = (barHeight - 3) + "px sans-serif";
+        context.textBaseline = "top";
+        this._dotsWidth = context.measureText("\u2026").width;
+        var visibleTimeLeft = this._timeWindowLeft - this._paddingLeftTime;
+
+        var anchorBox = new AnchorBox();
+        for (var i = 0; i < timelineEntries.length; ++i) {
+            var entry = timelineEntries[i];
+            var startTime = entry.startTime;
+            if (startTime > this._timeWindowRight)
+                break;
+            if ((startTime + entry.duration) < visibleTimeLeft)
+                continue;
+            this._entryToAnchorBox(entry, anchorBox);
+            if (anchorBox.width < this._minWidth)
+                continue;
+
+            var colorPair = entry.colorPair;
+            var color;
+            if (this._highlightedNodeIndex === i)
+                color =  colorPair.highlighted;
+            else
+                color = colorPair.normal;
+
+            context.beginPath();
+            context.rect(anchorBox.x, anchorBox.y, anchorBox.width - 1, anchorBox.height - 1);
+            context.fillStyle = color;
+            context.fill();
+
+            var xText = Math.max(0, anchorBox.x);
+            var widthText = anchorBox.width - textPaddingLeft + anchorBox.x - xText;
+            var title = this._prepareText(context, entry.node.functionName, widthText);
+            if (title) {
+                context.fillStyle = "#333";
+                context.fillText(title, xText + textPaddingLeft, anchorBox.y - 1);
+            }
+        }
+
+        var entryInfo = this._prepareHighlightedEntryInfo();
+        if (entryInfo)
+            this._printEntryInfo(context, entryInfo, 0, 25);
+    },
+
+    _printEntryInfo: function(context, entryInfo, x, y)
+    {
+        const lineHeight = 18;
+        const maxTextWidth = 290;
+        const paddingLeft = 10;
+        const paddingTop = 5;
+        const paddingLeftText = 10;
+        var maxTitleWidth = 0;
+        context.font = "bold " + (this._barHeight - 3) + "px sans-serif";
+        for (var i = 0; i < entryInfo.length; ++i)
+            maxTitleWidth = Math.max(maxTitleWidth, context.measureText(entryInfo[i].title).width);
+
+        context.beginPath();
+        context.rect(x, y, maxTextWidth + 5, lineHeight * entryInfo.length + 5);
+        context.strokeStyle = "rgba(0,0,0,0)";
+        context.fillStyle = "rgba(254,254,254,0.8)";
+        context.fill();
+        context.stroke();
+
+        context.fillStyle = "#333";
+        for (var i = 0; i < entryInfo.length; ++i)
+            context.fillText(entryInfo[i].title, x + paddingLeft, y + lineHeight * i);
+
+        context.font = (this._barHeight - 3) + "px sans-serif";
+        for (var i = 0; i < entryInfo.length; ++i) {
+            var text = this._prepareText(context, entryInfo[i].text, maxTextWidth - maxTitleWidth - 2 * paddingLeft);
+            context.fillText(text, x + paddingLeft + maxTitleWidth + paddingLeft, y + lineHeight * i);
+        }
+    },
+
+    _prepareText: function(context, title, maxSize)
+    {
+        if (maxSize < this._dotsWidth)
+            return null;
+        var titleWidth = context.measureText(title).width;
+        if (maxSize > titleWidth)
+            return title;
+        maxSize -= this._dotsWidth;
+        var dotRegExp=/[\.\$]/g;
+        var match = dotRegExp.exec(title);
+        if (!match) {
+            var visiblePartSize = maxSize / titleWidth;
+            var newTextLength = Math.floor(title.length * visiblePartSize) + 1;
+            var minTextLength = 4;
+            if (newTextLength < minTextLength)
+                return null;
+            var substring;
+            do {
+                --newTextLength;
+                substring = title.substring(0, newTextLength);
+            } while (context.measureText(substring).width > maxSize);
+            return title.substring(0, newTextLength) + "\u2026";
+        }
+        while (match) {
+            var substring = title.substring(match.index + 1);
+            var width = context.measureText(substring).width;
+            if (maxSize > width)
+                return "\u2026" + substring;
+            match = dotRegExp.exec(title);
+        }
+        var i = 0;
+        do {
+            ++i;
+        } while (context.measureText(title.substring(0, i)).width < maxSize);
+        return title.substring(0, i - 1) + "\u2026";
+    },
+
+    _scheduleUpdate: function()
+    {
+        if (this._updateTimerId)
+            return;
+        this._updateTimerId = setTimeout(this.update.bind(this), 10);
+    },
+    
+    _updateBoundaries: function()
+    {
+        this._windowLeft = this._overviewGrid.windowLeft();
+        this._windowRight = this._overviewGrid.windowRight();
+        this._windowWidth = this._windowRight - this._windowLeft;
+
+        this._totalTime = this._timelineData.totalTime;
+        this._timeWindowLeft = this._windowLeft * this._totalTime;
+        this._timeWindowRight = this._windowRight * this._totalTime;
+
+        this._pixelWindowWidth = this._chartContainer.clientWidth;
+        this._totalPixels = Math.floor(this._pixelWindowWidth / this._windowWidth);
+        this._pixelWindowLeft = Math.floor(this._totalPixels * this._windowLeft);
+        this._pixelWindowRight = Math.floor(this._totalPixels * this._windowRight);
+
+        this._timeToPixel = this._totalPixels / this._totalTime;
+        this._pixelToTime = this._totalTime / this._totalPixels;
+        this._paddingLeftTime = this._paddingLeft / this._timeToPixel;
+    },
+
+    update: function()
+    {
+        this._updateTimerId = 0;
+        if (!this._timelineData)
+            this._calculateTimelineData();
+        if (!this._timelineData)
+            return;
+        this._updateBoundaries();
+        this.draw(this._chartContainer.clientWidth, this._chartContainer.clientHeight);
+        this._calculator._updateBoundaries(this);
+        this._overviewCalculator._updateBoundaries(this);
+        this._timelineGrid.element.style.width = this.element.clientWidth;
+        this._timelineGrid.updateDividers(this._calculator);
+        this._overviewGrid.updateDividers(this._overviewCalculator);
+        if (this._updateOverviewCanvas) {
+            this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
+            this._updateOverviewCanvas = false;
+        }
+    },
+
+    __proto__: WebInspector.View.prototype
+};
diff --git a/Source/devtools/front_end/FontView.js b/Source/devtools/front_end/FontView.js
new file mode 100644
index 0000000..64dbd5b
--- /dev/null
+++ b/Source/devtools/front_end/FontView.js
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2007, 2008 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @extends {WebInspector.ResourceView}
+ * @constructor
+ */
+WebInspector.FontView = function(resource)
+{
+    WebInspector.ResourceView.call(this, resource);
+
+    this.element.addStyleClass("font");
+}
+
+WebInspector.FontView._fontPreviewLines = [ "ABCDEFGHIJKLM", "NOPQRSTUVWXYZ", "abcdefghijklm", "nopqrstuvwxyz", "1234567890" ];
+
+WebInspector.FontView._fontId = 0;
+
+WebInspector.FontView._measureFontSize = 50;
+
+WebInspector.FontView.prototype = {
+    hasContent: function()
+    {
+        return true;
+    },
+
+    _createContentIfNeeded: function()
+    {
+        if (this.fontPreviewElement)
+            return;
+
+        var uniqueFontName = "WebInspectorFontPreview" + (++WebInspector.FontView._fontId);
+
+        this.fontStyleElement = document.createElement("style");
+        this.fontStyleElement.textContent = "@font-face { font-family: \"" + uniqueFontName + "\"; src: url(" + this.resource.url + "); }";
+        document.head.appendChild(this.fontStyleElement);
+
+        var fontPreview = document.createElement("div");
+        for (var i = 0; i < WebInspector.FontView._fontPreviewLines.length; ++i) {
+            if (i > 0)
+                fontPreview.appendChild(document.createElement("br"));
+            fontPreview.appendChild(document.createTextNode(WebInspector.FontView._fontPreviewLines[i]));
+        }
+        this.fontPreviewElement = fontPreview.cloneNode(true);
+        this.fontPreviewElement.style.setProperty("font-family", uniqueFontName);
+        this.fontPreviewElement.style.setProperty("visibility", "hidden");
+
+        this._dummyElement = fontPreview;
+        this._dummyElement.style.visibility = "hidden";
+        this._dummyElement.style.zIndex = "-1";
+        this._dummyElement.style.display = "inline";
+        this._dummyElement.style.position = "absolute";
+        this._dummyElement.style.setProperty("font-family", uniqueFontName);
+        this._dummyElement.style.setProperty("font-size", WebInspector.FontView._measureFontSize + "px");
+
+        this.element.appendChild(this.fontPreviewElement);
+    },
+
+    wasShown: function()
+    {
+        this._createContentIfNeeded();
+
+        this.updateFontPreviewSize();
+    },
+
+    onResize: function()
+    {
+        if (this._inResize)
+            return;
+
+        this._inResize = true;
+        try {
+            this.updateFontPreviewSize();
+        } finally {
+            delete this._inResize;
+        }
+    },
+
+    _measureElement: function()
+    {
+        this.element.appendChild(this._dummyElement);
+        var result = { width: this._dummyElement.offsetWidth, height: this._dummyElement.offsetHeight };
+        this.element.removeChild(this._dummyElement);
+
+        return result;
+    },
+
+    updateFontPreviewSize: function()
+    {
+        if (!this.fontPreviewElement || !this.isShowing())
+            return;
+
+        this.fontPreviewElement.style.removeProperty("visibility");
+        var dimension = this._measureElement();
+
+        const height = dimension.height;
+        const width = dimension.width;
+
+        // Subtract some padding. This should match the paddings in the CSS plus room for the scrollbar.
+        const containerWidth = this.element.offsetWidth - 50;
+        const containerHeight = this.element.offsetHeight - 30;
+
+        if (!height || !width || !containerWidth || !containerHeight) {
+            this.fontPreviewElement.style.removeProperty("font-size");
+            return;
+        }
+
+        var widthRatio = containerWidth / width;
+        var heightRatio = containerHeight / height;
+        var finalFontSize = Math.floor(WebInspector.FontView._measureFontSize * Math.min(widthRatio, heightRatio)) - 2;
+
+        this.fontPreviewElement.style.setProperty("font-size", finalFontSize + "px", null);
+    },
+
+    __proto__: WebInspector.ResourceView.prototype
+}
diff --git a/Source/devtools/front_end/GoToLineDialog.js b/Source/devtools/front_end/GoToLineDialog.js
new file mode 100644
index 0000000..4a1891f
--- /dev/null
+++ b/Source/devtools/front_end/GoToLineDialog.js
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.DialogDelegate}
+ */
+WebInspector.GoToLineDialog = function(view)
+{
+    WebInspector.DialogDelegate.call(this);
+    
+    this.element = document.createElement("div");
+    this.element.className = "go-to-line-dialog";
+
+    this.element.createChild("label").textContent = WebInspector.UIString("Go to line: ");
+
+    this._input = this.element.createChild("input");
+    this._input.setAttribute("type", "text");
+    this._input.setAttribute("size", 6);
+
+    this._goButton = this.element.createChild("button");
+    this._goButton.textContent = WebInspector.UIString("Go");
+    this._goButton.addEventListener("click", this._onGoClick.bind(this), false);
+
+    this._view = view;
+}
+
+/**
+ * @param {WebInspector.Panel} panel
+ * @param {function():?WebInspector.View} viewGetter
+ */
+WebInspector.GoToLineDialog.install = function(panel, viewGetter)
+{
+    var goToLineShortcut = WebInspector.GoToLineDialog.createShortcut();
+    panel.registerShortcuts([goToLineShortcut], WebInspector.GoToLineDialog._show.bind(null, viewGetter));
+}
+
+/**
+ * @param {function():?WebInspector.View} viewGetter
+ * @param {Event=} event
+ * @return {boolean}
+ */
+WebInspector.GoToLineDialog._show = function(viewGetter, event)
+{
+    var sourceView = viewGetter();
+    if (!sourceView || !sourceView.canHighlightLine())
+        return false;
+    WebInspector.Dialog.show(sourceView.element, new WebInspector.GoToLineDialog(sourceView));
+    return true;
+}
+
+/**
+ * @return {!WebInspector.KeyboardShortcut.Descriptor}
+ */
+WebInspector.GoToLineDialog.createShortcut = function()
+{
+    var isMac = WebInspector.isMac();
+    var shortcut;
+    if (isMac)
+        return WebInspector.KeyboardShortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Meta);
+    return WebInspector.KeyboardShortcut.makeDescriptor("g", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
+}
+
+WebInspector.GoToLineDialog.prototype = {
+    focus: function()
+    {
+        WebInspector.setCurrentFocusElement(this._input);
+        this._input.select();
+    },
+    
+    _onGoClick: function()
+    {
+        this._applyLineNumber();
+        WebInspector.Dialog.hide();
+    },
+    
+    _applyLineNumber: function()
+    {
+        var value = this._input.value;
+        var lineNumber = parseInt(value, 10) - 1;
+        if (!isNaN(lineNumber) && lineNumber >= 0)
+            this._view.highlightLine(lineNumber);
+    },
+    
+    onEnter: function()
+    {
+        this._applyLineNumber();
+    },
+
+    __proto__: WebInspector.DialogDelegate.prototype
+}
diff --git a/Source/devtools/front_end/HAREntry.js b/Source/devtools/front_end/HAREntry.js
new file mode 100644
index 0000000..c6dc488
--- /dev/null
+++ b/Source/devtools/front_end/HAREntry.js
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+// See http://groups.google.com/group/http-archive-specification/web/har-1-2-spec
+// for HAR specification.
+
+// FIXME: Some fields are not yet supported due to back-end limitations.
+// See https://bugs.webkit.org/show_bug.cgi?id=58127 for details.
+
+/**
+ * @constructor
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.HAREntry = function(request)
+{
+    this._request = request;
+}
+
+WebInspector.HAREntry.prototype = {
+    /**
+     * @return {Object}
+     */
+    build: function()
+    {
+        var entry =  {
+            startedDateTime: new Date(this._request.startTime * 1000),
+            time: WebInspector.HAREntry._toMilliseconds(this._request.duration),
+            request: this._buildRequest(),
+            response: this._buildResponse(),
+            cache: { }, // Not supported yet.
+            timings: this._buildTimings()
+        };
+        var page = WebInspector.networkLog.pageLoadForRequest(this._request);
+        if (page)
+            entry.pageref = "page_" + page.id;
+        return entry;
+    },
+
+    /**
+     * @return {Object}
+     */
+    _buildRequest: function()
+    {
+        var res = {
+            method: this._request.requestMethod,
+            url: this._buildRequestURL(this._request.url),
+            httpVersion: this._request.requestHttpVersion,
+            headers: this._request.requestHeaders,
+            queryString: this._buildParameters(this._request.queryParameters || []),
+            cookies: this._buildCookies(this._request.requestCookies || []),
+            headersSize: this._request.requestHeadersSize,
+            bodySize: this.requestBodySize
+        };
+        if (this._request.requestFormData)
+            res.postData = this._buildPostData();
+
+        return res;
+    },
+
+    /**
+     * @return {Object}
+     */
+    _buildResponse: function()
+    {
+        return {
+            status: this._request.statusCode,
+            statusText: this._request.statusText,
+            httpVersion: this._request.responseHttpVersion,
+            headers: this._request.responseHeaders,
+            cookies: this._buildCookies(this._request.responseCookies || []),
+            content: this._buildContent(),
+            redirectURL: this._request.responseHeaderValue("Location") || "",
+            headersSize: this._request.responseHeadersSize,
+            bodySize: this.responseBodySize
+        };
+    },
+
+    /**
+     * @return {Object}
+     */
+    _buildContent: function()
+    {
+        var content = {
+            size: this._request.resourceSize,
+            mimeType: this._request.mimeType,
+            // text: this._request.content // TODO: pull out into a boolean flag, as content can be huge (and needs to be requested with an async call)
+        };
+        var compression = this.responseCompression;
+        if (typeof compression === "number")
+            content.compression = compression;
+        return content;
+    },
+
+    /**
+     * @return {Object}
+     */
+    _buildTimings: function()
+    {
+        var waitForConnection = this._interval("connectStart", "connectEnd");
+        var blocked;
+        var connect;
+        var dns = this._interval("dnsStart", "dnsEnd");
+        var send = this._interval("sendStart", "sendEnd");
+        var ssl = this._interval("sslStart", "sslEnd");
+
+        if (ssl !== -1 && send !== -1)
+            send -= ssl;
+
+        if (this._request.connectionReused) {
+            connect = -1;
+            blocked = waitForConnection;
+        } else {
+            blocked = 0;
+            connect = waitForConnection;
+            if (dns !== -1)
+                connect -= dns;
+        }
+
+        return {
+            blocked: blocked,
+            dns: dns,
+            connect: connect,
+            send: send,
+            wait: this._interval("sendEnd", "receiveHeadersEnd"),
+            receive: WebInspector.HAREntry._toMilliseconds(this._request.receiveDuration),
+            ssl: ssl
+        };
+    },
+
+    /**
+     * @return {Object}
+     */
+    _buildPostData: function()
+    {
+        var res = {
+            mimeType: this._request.requestHeaderValue("Content-Type"),
+            text: this._request.requestFormData
+        };
+        if (this._request.formParameters)
+            res.params = this._buildParameters(this._request.formParameters);
+        return res;
+    },
+
+    /**
+     * @param {Array.<Object>} parameters
+     * @return {Array.<Object>}
+     */
+    _buildParameters: function(parameters)
+    {
+        return parameters.slice();
+    },
+
+    /**
+     * @param {string} url
+     * @return {string}
+     */
+    _buildRequestURL: function(url)
+    {
+        return url.split("#", 2)[0];
+    },
+
+    /**
+     * @param {Array.<WebInspector.Cookie>} cookies
+     * @return {Array.<Object>}
+     */
+    _buildCookies: function(cookies)
+    {
+        return cookies.map(this._buildCookie.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.Cookie} cookie
+     * @return {Object}
+     */
+    _buildCookie: function(cookie)
+    {
+        return {
+            name: cookie.name(),
+            value: cookie.value(),
+            path: cookie.path(),
+            domain: cookie.domain(),
+            expires: cookie.expiresDate(new Date(this._request.startTime * 1000)),
+            httpOnly: cookie.httpOnly(),
+            secure: cookie.secure()
+        };
+    },
+
+    /**
+     * @param {string} start
+     * @param {string} end
+     * @return {number}
+     */
+    _interval: function(start, end)
+    {
+        var timing = this._request.timing;
+        if (!timing)
+            return -1;
+        var startTime = timing[start];
+        return typeof startTime !== "number" || startTime === -1 ? -1 : Math.round(timing[end] - startTime);
+    },
+
+    /**
+     * @return {number}
+     */
+    get requestBodySize()
+    {
+        return !this._request.requestFormData ? 0 : this._request.requestFormData.length;
+    },
+
+    /**
+     * @return {number}
+     */
+    get responseBodySize()
+    {
+        if (this._request.cached || this._request.statusCode === 304)
+            return 0;
+        return this._request.transferSize - this._request.responseHeadersSize
+    },
+
+    /**
+     * @return {number|undefined}
+     */
+    get responseCompression()
+    {
+        if (this._request.cached || this._request.statusCode === 304)
+            return;
+        return this._request.resourceSize - (this._request.transferSize - this._request.responseHeadersSize);
+    }
+}
+
+/**
+ * @param {number} time
+ * @return {number}
+ */
+WebInspector.HAREntry._toMilliseconds = function(time)
+{
+    return time === -1 ? -1 : Math.round(time * 1000);
+}
+
+/**
+ * @constructor
+ * @param {Array.<WebInspector.NetworkRequest>} requests
+ */
+WebInspector.HARLog = function(requests)
+{
+    this._requests = requests;
+}
+
+WebInspector.HARLog.prototype = {
+    /**
+     * @return {Object}
+     */
+    build: function()
+    {
+        return {
+            version: "1.2",
+            creator: this._creator(),
+            pages: this._buildPages(),
+            entries: this._requests.map(this._convertResource.bind(this))
+        }
+    },
+
+    _creator: function()
+    {
+        var webKitVersion = /AppleWebKit\/([^ ]+)/.exec(window.navigator.userAgent);
+
+        return {
+            name: "WebInspector",
+            version: webKitVersion ? webKitVersion[1] : "n/a"
+        };
+    },
+
+    /**
+     * @return {Array}
+     */
+    _buildPages: function()
+    {
+        var seenIdentifiers = {};
+        var pages = [];
+        for (var i = 0; i < this._requests.length; ++i) {
+            var page = WebInspector.networkLog.pageLoadForRequest(this._requests[i]);
+            if (!page || seenIdentifiers[page.id])
+                continue;
+            seenIdentifiers[page.id] = true;
+            pages.push(this._convertPage(page));
+        }
+        return pages;
+    },
+
+    /**
+     * @param {WebInspector.PageLoad} page
+     * @return {Object}
+     */
+    _convertPage: function(page)
+    {
+        return {
+            startedDateTime: new Date(page.startTime * 1000),
+            id: "page_" + page.id,
+            title: page.url, // We don't have actual page title here. URL is probably better than nothing.
+            pageTimings: {
+                onContentLoad: this._pageEventTime(page, page.contentLoadTime),
+                onLoad: this._pageEventTime(page, page.loadTime)
+            }
+        }
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} request
+     * @return {Object}
+     */
+    _convertResource: function(request)
+    {
+        return (new WebInspector.HAREntry(request)).build();
+    },
+
+    /**
+     * @param {WebInspector.PageLoad} page
+     * @param {number} time
+     * @return {number}
+     */
+    _pageEventTime: function(page, time)
+    {
+        var startTime = page.startTime;
+        if (time === -1 || startTime === -1)
+            return -1;
+        return WebInspector.HAREntry._toMilliseconds(time - startTime);
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HARWriter = function()
+{
+}
+
+WebInspector.HARWriter.prototype = {
+    /**
+     * @param {WebInspector.OutputStream} stream
+     * @param {Array.<WebInspector.NetworkRequest>} requests
+     * @param {WebInspector.Progress} progress
+     */
+    write: function(stream, requests, progress)
+    {
+        this._stream = stream;
+        this._harLog = (new WebInspector.HARLog(requests)).build();
+        this._pendingRequests = 1; // Guard against completing resource transfer before all requests are made.
+        var entries = this._harLog.entries;
+        for (var i = 0; i < entries.length; ++i) {
+            var content = requests[i].content;
+            if (typeof content === "undefined" && requests[i].finished) {
+                ++this._pendingRequests;
+                requests[i].requestContent(this._onContentAvailable.bind(this, entries[i]));
+            } else if (content !== null)
+                entries[i].response.content.text = content;
+        }
+        var compositeProgress = new WebInspector.CompositeProgress(progress);
+        this._writeProgress = compositeProgress.createSubProgress();
+        if (--this._pendingRequests) {
+            this._requestsProgress = compositeProgress.createSubProgress();
+            this._requestsProgress.setTitle(WebInspector.UIString("Collecting content…"));
+            this._requestsProgress.setTotalWork(this._pendingRequests);
+        } else
+            this._beginWrite();
+    },
+
+    /**
+     * @param {Object} entry
+     * @param {string|null} content
+     * @param {boolean} contentEncoded
+     * @param {string=} mimeType
+     */
+    _onContentAvailable: function(entry, content, contentEncoded, mimeType)
+    {
+        if (content !== null)
+            entry.response.content.text = content;
+        if (this._requestsProgress)
+            this._requestsProgress.worked();
+        if (!--this._pendingRequests) {
+            this._requestsProgress.done();
+            this._beginWrite();
+        }
+    },
+
+    _beginWrite: function()
+    {
+        const jsonIndent = 2;
+        this._text = JSON.stringify({log: this._harLog}, null, jsonIndent);
+        this._writeProgress.setTitle(WebInspector.UIString("Writing file…"));
+        this._writeProgress.setTotalWork(this._text.length);
+        this._bytesWritten = 0;
+        this._writeNextChunk(this._stream);
+    },
+
+    /**
+     * @param {WebInspector.OutputStream} stream
+     * @param {string=} error
+     */
+    _writeNextChunk: function(stream, error)
+    {
+        if (this._bytesWritten >= this._text.length || error) {
+            stream.close();
+            this._writeProgress.done();
+            return;
+        }
+        const chunkSize = 100000;
+        var text = this._text.substring(this._bytesWritten, this._bytesWritten + chunkSize);
+        this._bytesWritten += text.length;
+        stream.write(text, this._writeNextChunk.bind(this));
+        this._writeProgress.setWorked(this._bytesWritten);
+    }
+}
diff --git a/Source/devtools/front_end/HandlerRegistry.js b/Source/devtools/front_end/HandlerRegistry.js
new file mode 100644
index 0000000..b7a2cd5
--- /dev/null
+++ b/Source/devtools/front_end/HandlerRegistry.js
@@ -0,0 +1,234 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @implements {WebInspector.ContextMenu.Provider}
+ */
+WebInspector.HandlerRegistry = function(setting)
+{
+    WebInspector.Object.call(this);
+    this._handlers = {};
+    this._setting = setting;
+    this._activeHandler = this._setting.get();
+    WebInspector.ContextMenu.registerProvider(this);
+}
+
+WebInspector.HandlerRegistry.prototype = {
+    get handlerNames()
+    {
+        return Object.getOwnPropertyNames(this._handlers);
+    },
+
+    get activeHandler()
+    {
+        return this._activeHandler;
+    },
+
+    set activeHandler(value)
+    {
+        this._activeHandler = value;
+        this._setting.set(value);
+    },
+
+    /**
+     * @param {Object} data
+     */
+    dispatch: function(data)
+    {
+        return this.dispatchToHandler(this._activeHandler, data);
+    },
+
+    /**
+     * @param {string} name
+     * @param {Object} data
+     */
+    dispatchToHandler: function(name, data)
+    {
+        var handler = this._handlers[name];
+        var result = handler && handler(data);
+        return !!result;
+    },
+
+    registerHandler: function(name, handler)
+    {
+        this._handlers[name] = handler;
+        this.dispatchEventToListeners(WebInspector.HandlerRegistry.EventTypes.HandlersUpdated);
+    },
+
+    unregisterHandler: function(name)
+    {
+        delete this._handlers[name];
+        this.dispatchEventToListeners(WebInspector.HandlerRegistry.EventTypes.HandlersUpdated);
+    },
+
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    appendApplicableItems: function(event, contextMenu, target)
+    {
+        if (event.hasBeenHandledByHandlerRegistry)
+            return;
+        event.hasBeenHandledByHandlerRegistry = true;
+        this._appendContentProviderItems(contextMenu, target);
+        this._appendHrefItems(contextMenu, target);
+    },
+
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    _appendContentProviderItems: function(contextMenu, target)
+    {
+        if (!(target instanceof WebInspector.UISourceCode || target instanceof WebInspector.Resource || target instanceof WebInspector.NetworkRequest))
+            return;
+        var contentProvider = /** @type {WebInspector.ContentProvider} */ (target);
+        if (!contentProvider.contentURL())
+            return;
+
+        contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), WebInspector.openResource.bind(WebInspector, contentProvider.contentURL(), false));
+        // Skip 0th handler, as it's 'Use default panel' one.
+        for (var i = 1; i < this.handlerNames.length; ++i) {
+            var handler = this.handlerNames[i];
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open using %s" : "Open Using %s", handler),
+                this.dispatchToHandler.bind(this, handler, { url: contentProvider.contentURL() }));
+        }
+        contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), InspectorFrontendHost.copyText.bind(InspectorFrontendHost, contentProvider.contentURL()));
+
+        if (!contentProvider.contentURL())
+            return;
+
+        var contentType = contentProvider.contentType();
+        if (contentType !== WebInspector.resourceTypes.Document &&
+            contentType !== WebInspector.resourceTypes.Stylesheet &&
+            contentType !== WebInspector.resourceTypes.Script)
+            return;
+
+        function doSave(forceSaveAs, content)
+        {
+            var url = contentProvider.contentURL();
+            WebInspector.fileManager.save(url, content, forceSaveAs);
+            WebInspector.fileManager.close(url);
+        }
+
+        function save(forceSaveAs)
+        {
+            if (contentProvider instanceof WebInspector.UISourceCode) {
+                var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (contentProvider);
+                if (uiSourceCode.isDirty()) {
+                    doSave(forceSaveAs, uiSourceCode.workingCopy());
+                    uiSourceCode.commitWorkingCopy(function() { });
+                    return;
+                }
+            }
+            contentProvider.requestContent(doSave.bind(this, forceSaveAs));
+        }
+
+        contextMenu.appendSeparator();
+        contextMenu.appendItem(WebInspector.UIString("Save"), save.bind(this, false));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save as..." : "Save As..."), save.bind(this, true));
+    },
+
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    _appendHrefItems: function(contextMenu, target)
+    {
+        if (!(target instanceof Node))
+            return;
+        var targetNode = /** @type {Node} */ (target);
+
+        var anchorElement = targetNode.enclosingNodeOrSelfWithClass("webkit-html-resource-link") || targetNode.enclosingNodeOrSelfWithClass("webkit-html-external-link");
+        if (!anchorElement)
+            return;
+
+        var resourceURL = anchorElement.href;
+        if (!resourceURL)
+            return;
+
+        // Add resource-related actions.
+        contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), WebInspector.openResource.bind(WebInspector, resourceURL, false));
+        if (WebInspector.resourceForURL(resourceURL))
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open link in Resources panel" : "Open Link in Resources Panel"), WebInspector.openResource.bind(null, resourceURL, true));
+        contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), InspectorFrontendHost.copyText.bind(InspectorFrontendHost, resourceURL));
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+
+WebInspector.HandlerRegistry.EventTypes = {
+    HandlersUpdated: "HandlersUpdated"
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HandlerSelector = function(handlerRegistry)
+{
+    this._handlerRegistry = handlerRegistry;
+    this.element = document.createElement("select");
+    this.element.addEventListener("change", this._onChange.bind(this), false);
+    this._update();
+    this._handlerRegistry.addEventListener(WebInspector.HandlerRegistry.EventTypes.HandlersUpdated, this._update.bind(this));
+}
+
+WebInspector.HandlerSelector.prototype =
+{
+    _update: function()
+    {
+        this.element.removeChildren();
+        var names = this._handlerRegistry.handlerNames;
+        var activeHandler = this._handlerRegistry.activeHandler;
+
+        for (var i = 0; i < names.length; ++i) {
+            var option = document.createElement("option");
+            option.textContent = names[i];
+            option.selected = activeHandler === names[i];
+            this.element.appendChild(option);
+        }
+        this.element.disabled = names.length <= 1;
+    },
+
+    _onChange: function(event)
+    {
+        var value = event.target.value;
+        this._handlerRegistry.activeHandler = value;
+    }
+}
+
+
+/**
+ * @type {WebInspector.HandlerRegistry}
+ */
+WebInspector.openAnchorLocationRegistry = null;
diff --git a/Source/devtools/front_end/HeapSnapshot.js b/Source/devtools/front_end/HeapSnapshot.js
new file mode 100644
index 0000000..517e737
--- /dev/null
+++ b/Source/devtools/front_end/HeapSnapshot.js
@@ -0,0 +1,1738 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotArraySlice = function(array, start, end)
+{
+    this._array = array;
+    this._start = start;
+    this.length = end - start;
+}
+
+WebInspector.HeapSnapshotArraySlice.prototype = {
+    item: function(index)
+    {
+        return this._array[this._start + index];
+    },
+
+    slice: function(start, end)
+    {
+        if (typeof end === "undefined")
+            end = this.length;
+        return this._array.subarray(this._start + start, this._start + end);
+    }
+}
+
+/**
+ * @constructor
+ * @param {number=} edgeIndex
+ */
+WebInspector.HeapSnapshotEdge = function(snapshot, edges, edgeIndex)
+{
+    this._snapshot = snapshot;
+    this._edges = edges;
+    this.edgeIndex = edgeIndex || 0;
+}
+
+WebInspector.HeapSnapshotEdge.prototype = {
+    clone: function()
+    {
+        return new WebInspector.HeapSnapshotEdge(this._snapshot, this._edges, this.edgeIndex);
+    },
+
+    hasStringName: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    name: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    node: function()
+    {
+        return this._snapshot.createNode(this.nodeIndex());
+    },
+
+    nodeIndex: function()
+    {
+        return this._edges.item(this.edgeIndex + this._snapshot._edgeToNodeOffset);
+    },
+
+    rawEdges: function()
+    {
+        return this._edges;
+    },
+
+    toString: function()
+    {
+        return "HeapSnapshotEdge: " + this.name();
+    },
+
+    type: function()
+    {
+        return this._snapshot._edgeTypes[this._type()];
+    },
+
+    serialize: function()
+    {
+        var node = this.node();
+        return {
+            name: this.name(),
+            node: node.serialize(),
+            nodeIndex: this.nodeIndex(),
+            type: this.type(),
+            distance: node.distance()
+        };
+    },
+
+    _type: function()
+    {
+        return this._edges.item(this.edgeIndex + this._snapshot._edgeTypeOffset);
+    }
+};
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotEdgeIterator = function(edge)
+{
+    this.edge = edge;
+}
+
+WebInspector.HeapSnapshotEdgeIterator.prototype = {
+    rewind: function()
+    {
+        this.edge.edgeIndex = 0;
+    },
+
+    hasNext: function()
+    {
+        return this.edge.edgeIndex < this.edge._edges.length;
+    },
+
+    index: function()
+    {
+        return this.edge.edgeIndex;
+    },
+
+    setIndex: function(newIndex)
+    {
+        this.edge.edgeIndex = newIndex;
+    },
+
+    item: function()
+    {
+        return this.edge;
+    },
+
+    next: function()
+    {
+        this.edge.edgeIndex += this.edge._snapshot._edgeFieldsCount;
+    }
+};
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotRetainerEdge = function(snapshot, retainedNodeIndex, retainerIndex)
+{
+    this._snapshot = snapshot;
+    this._retainedNodeIndex = retainedNodeIndex;
+
+    var retainedNodeOrdinal = retainedNodeIndex / snapshot._nodeFieldCount;
+    this._firstRetainer = snapshot._firstRetainerIndex[retainedNodeOrdinal];
+    this._retainersCount = snapshot._firstRetainerIndex[retainedNodeOrdinal + 1] - this._firstRetainer;
+
+    this.setRetainerIndex(retainerIndex);
+}
+
+WebInspector.HeapSnapshotRetainerEdge.prototype = {
+    clone: function()
+    {
+        return new WebInspector.HeapSnapshotRetainerEdge(this._snapshot, this._retainedNodeIndex, this.retainerIndex());
+    },
+
+    hasStringName: function()
+    {
+        return this._edge().hasStringName();
+    },
+
+    name: function()
+    {
+        return this._edge().name();
+    },
+
+    node: function()
+    {
+        return this._node();
+    },
+
+    nodeIndex: function()
+    {
+        return this._nodeIndex;
+    },
+
+    retainerIndex: function()
+    {
+        return this._retainerIndex;
+    },
+
+    setRetainerIndex: function(newIndex)
+    {
+        if (newIndex !== this._retainerIndex) {
+            this._retainerIndex = newIndex;
+            this.edgeIndex = newIndex;
+        }
+    },
+
+    set edgeIndex(edgeIndex)
+    {
+        var retainerIndex = this._firstRetainer + edgeIndex;
+        this._globalEdgeIndex = this._snapshot._retainingEdges[retainerIndex];
+        this._nodeIndex = this._snapshot._retainingNodes[retainerIndex];
+        delete this._edgeInstance;
+        delete this._nodeInstance;
+    },
+
+    _node: function()
+    {
+        if (!this._nodeInstance)
+            this._nodeInstance = this._snapshot.createNode(this._nodeIndex);
+        return this._nodeInstance;
+    },
+
+    _edge: function()
+    {
+        if (!this._edgeInstance) {
+            var edgeIndex = this._globalEdgeIndex - this._node()._edgeIndexesStart();
+            this._edgeInstance = this._snapshot.createEdge(this._node().rawEdges(), edgeIndex);
+        }
+        return this._edgeInstance;
+    },
+
+    toString: function()
+    {
+        return this._edge().toString();
+    },
+
+    serialize: function()
+    {
+        var node = this.node();
+        return {
+            name: this.name(),
+            node: node.serialize(),
+            nodeIndex: this.nodeIndex(),
+            type: this.type(),
+            distance: node.distance()
+        };
+    },
+
+    type: function()
+    {
+        return this._edge().type();
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotRetainerEdgeIterator = function(retainer)
+{
+    this.retainer = retainer;
+}
+
+WebInspector.HeapSnapshotRetainerEdgeIterator.prototype = {
+    rewind: function()
+    {
+        this.retainer.setRetainerIndex(0);
+    },
+
+    hasNext: function()
+    {
+        return this.retainer.retainerIndex() < this.retainer._retainersCount;
+    },
+
+    index: function()
+    {
+        return this.retainer.retainerIndex();
+    },
+
+    setIndex: function(newIndex)
+    {
+        this.retainer.setRetainerIndex(newIndex);
+    },
+
+    item: function()
+    {
+        return this.retainer;
+    },
+
+    next: function()
+    {
+        this.retainer.setRetainerIndex(this.retainer.retainerIndex() + 1);
+    }
+};
+
+/**
+ * @constructor
+ * @param {number=} nodeIndex
+ */
+WebInspector.HeapSnapshotNode = function(snapshot, nodeIndex)
+{
+    this._snapshot = snapshot;
+    this._firstNodeIndex = nodeIndex;
+    this.nodeIndex = nodeIndex;
+}
+
+WebInspector.HeapSnapshotNode.prototype = {
+    distance: function()
+    {
+        return this._snapshot._nodeDistances[this.nodeIndex / this._snapshot._nodeFieldCount];
+    },
+
+    className: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    classIndex: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    dominatorIndex: function()
+    {
+        var nodeFieldCount = this._snapshot._nodeFieldCount;
+        return this._snapshot._dominatorsTree[this.nodeIndex / this._snapshot._nodeFieldCount] * nodeFieldCount;
+    },
+
+    edges: function()
+    {
+        return new WebInspector.HeapSnapshotEdgeIterator(this._snapshot.createEdge(this.rawEdges(), 0));
+    },
+
+    edgesCount: function()
+    {
+        return (this._edgeIndexesEnd() - this._edgeIndexesStart()) / this._snapshot._edgeFieldsCount;
+    },
+
+    id: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    isRoot: function()
+    {
+        return this.nodeIndex === this._snapshot._rootNodeIndex;
+    },
+
+    name: function()
+    {
+        return this._snapshot._strings[this._name()];
+    },
+
+    rawEdges: function()
+    {
+        return new WebInspector.HeapSnapshotArraySlice(this._snapshot._containmentEdges, this._edgeIndexesStart(), this._edgeIndexesEnd());
+    },
+
+    retainedSize: function()
+    {
+        var snapshot = this._snapshot;
+        return snapshot._nodes[this.nodeIndex + snapshot._nodeRetainedSizeOffset];
+    },
+
+    retainers: function()
+    {
+        return new WebInspector.HeapSnapshotRetainerEdgeIterator(this._snapshot.createRetainingEdge(this.nodeIndex, 0));
+    },
+
+    selfSize: function()
+    {
+        var snapshot = this._snapshot;
+        return snapshot._nodes[this.nodeIndex + snapshot._nodeSelfSizeOffset];
+    },
+
+    type: function()
+    {
+        return this._snapshot._nodeTypes[this._type()];
+    },
+
+    serialize: function()
+    {
+        return {
+            id: this.id(),
+            name: this.name(),
+            distance: this.distance(),
+            nodeIndex: this.nodeIndex,
+            retainedSize: this.retainedSize(),
+            selfSize: this.selfSize(),
+            type: this.type(),
+        };
+    },
+
+    _name: function()
+    {
+        var snapshot = this._snapshot;
+        return snapshot._nodes[this.nodeIndex + snapshot._nodeNameOffset];
+    },
+
+    _edgeIndexesStart: function()
+    {
+        return this._snapshot._firstEdgeIndexes[this._ordinal()];
+    },
+
+    _edgeIndexesEnd: function()
+    {
+        return this._snapshot._firstEdgeIndexes[this._ordinal() + 1];
+    },
+
+    _ordinal: function()
+    {
+        return this.nodeIndex / this._snapshot._nodeFieldCount;
+    },
+
+    _nextNodeIndex: function()
+    {
+        return this.nodeIndex + this._snapshot._nodeFieldCount;
+    },
+
+    _type: function()
+    {
+        var snapshot = this._snapshot;
+        return snapshot._nodes[this.nodeIndex + snapshot._nodeTypeOffset];
+    }
+};
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotNodeIterator = function(node)
+{
+    this.node = node;
+    this._nodesLength = node._snapshot._nodes.length;
+}
+
+WebInspector.HeapSnapshotNodeIterator.prototype = {
+    rewind: function()
+    {
+        this.node.nodeIndex = this.node._firstNodeIndex;
+    },
+
+    hasNext: function()
+    {
+        return this.node.nodeIndex < this._nodesLength;
+    },
+
+    index: function()
+    {
+        return this.node.nodeIndex;
+    },
+
+    setIndex: function(newIndex)
+    {
+        this.node.nodeIndex = newIndex;
+    },
+
+    item: function()
+    {
+        return this.node;
+    },
+
+    next: function()
+    {
+        this.node.nodeIndex = this.node._nextNodeIndex();
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshot = function(profile)
+{
+    this.uid = profile.snapshot.uid;
+    this._nodes = profile.nodes;
+    this._containmentEdges = profile.edges;
+    /** @type{HeapSnapshotMetainfo} */
+    this._metaNode = profile.snapshot.meta;
+    this._strings = profile.strings;
+
+    this._rootNodeIndex = 0;
+    if (profile.snapshot.root_index)
+        this._rootNodeIndex = profile.snapshot.root_index;
+
+    this._snapshotDiffs = {};
+    this._aggregatesForDiff = null;
+
+    this._init();
+}
+
+/**
+ * @constructor
+ */
+function HeapSnapshotMetainfo()
+{
+    // New format.
+    this.node_fields = [];
+    this.node_types = [];
+    this.edge_fields = [];
+    this.edge_types = [];
+    this.type_strings = {};
+
+    // Old format.
+    this.fields = [];
+    this.types = [];
+}
+
+/**
+ * @constructor
+ */
+function HeapSnapshotHeader()
+{
+    // New format.
+    this.title = "";
+    this.uid = 0;
+    this.meta = new HeapSnapshotMetainfo();
+    this.node_count = 0;
+    this.edge_count = 0;
+}
+
+WebInspector.HeapSnapshot.prototype = {
+    _init: function()
+    {
+        var meta = this._metaNode;
+
+        this._nodeTypeOffset = meta.node_fields.indexOf("type");
+        this._nodeNameOffset = meta.node_fields.indexOf("name");
+        this._nodeIdOffset = meta.node_fields.indexOf("id");
+        this._nodeSelfSizeOffset = meta.node_fields.indexOf("self_size");
+        this._nodeEdgeCountOffset = meta.node_fields.indexOf("edge_count");
+        this._nodeFieldCount = meta.node_fields.length;
+
+        this._nodeTypes = meta.node_types[this._nodeTypeOffset];
+        this._nodeHiddenType = this._nodeTypes.indexOf("hidden");
+        this._nodeObjectType = this._nodeTypes.indexOf("object");
+        this._nodeNativeType = this._nodeTypes.indexOf("native");
+        this._nodeCodeType = this._nodeTypes.indexOf("code");
+        this._nodeSyntheticType = this._nodeTypes.indexOf("synthetic");
+
+        this._edgeFieldsCount = meta.edge_fields.length;
+        this._edgeTypeOffset = meta.edge_fields.indexOf("type");
+        this._edgeNameOffset = meta.edge_fields.indexOf("name_or_index");
+        this._edgeToNodeOffset = meta.edge_fields.indexOf("to_node");
+
+        this._edgeTypes = meta.edge_types[this._edgeTypeOffset];
+        this._edgeTypes.push("invisible");
+        this._edgeElementType = this._edgeTypes.indexOf("element");
+        this._edgeHiddenType = this._edgeTypes.indexOf("hidden");
+        this._edgeInternalType = this._edgeTypes.indexOf("internal");
+        this._edgeShortcutType = this._edgeTypes.indexOf("shortcut");
+        this._edgeWeakType = this._edgeTypes.indexOf("weak");
+        this._edgeInvisibleType = this._edgeTypes.indexOf("invisible");
+
+        this.nodeCount = this._nodes.length / this._nodeFieldCount;
+        this._edgeCount = this._containmentEdges.length / this._edgeFieldsCount;
+
+        this._buildEdgeIndexes();
+        this._markInvisibleEdges();
+        this._buildRetainers();
+        this._calculateFlags();
+        this._calculateDistances();
+        var result = this._buildPostOrderIndex();
+        // Actually it is array that maps node ordinal number to dominator node ordinal number.
+        this._dominatorsTree = this._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex);
+        this._calculateRetainedSizes(result.postOrderIndex2NodeOrdinal);
+        this._buildDominatedNodes();
+    },
+
+    _buildEdgeIndexes: function()
+    {
+        var nodes = this._nodes;
+        var nodeCount = this.nodeCount;
+        var firstEdgeIndexes = this._firstEdgeIndexes = new Uint32Array(nodeCount + 1);
+        var nodeFieldCount = this._nodeFieldCount;
+        var edgeFieldsCount = this._edgeFieldsCount;
+        var nodeEdgeCountOffset = this._nodeEdgeCountOffset;
+        firstEdgeIndexes[nodeCount] = this._containmentEdges.length;
+        for (var nodeOrdinal = 0, edgeIndex = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) {
+            firstEdgeIndexes[nodeOrdinal] = edgeIndex;
+            edgeIndex += nodes[nodeOrdinal * nodeFieldCount + nodeEdgeCountOffset] * edgeFieldsCount;
+        }
+    },
+
+    _buildRetainers: function()
+    {
+        var retainingNodes = this._retainingNodes = new Uint32Array(this._edgeCount);
+        var retainingEdges = this._retainingEdges = new Uint32Array(this._edgeCount);
+        // Index of the first retainer in the _retainingNodes and _retainingEdges
+        // arrays. Addressed by retained node index.
+        var firstRetainerIndex = this._firstRetainerIndex = new Uint32Array(this.nodeCount + 1);
+
+        var containmentEdges = this._containmentEdges;
+        var edgeFieldsCount = this._edgeFieldsCount;
+        var nodeFieldCount = this._nodeFieldCount;
+        var edgeToNodeOffset = this._edgeToNodeOffset;
+        var nodes = this._nodes;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
+        var nodeCount = this.nodeCount;
+
+        for (var toNodeFieldIndex = edgeToNodeOffset, l = containmentEdges.length; toNodeFieldIndex < l; toNodeFieldIndex += edgeFieldsCount) {
+            var toNodeIndex = containmentEdges[toNodeFieldIndex];
+            if (toNodeIndex % nodeFieldCount)
+                throw new Error("Invalid toNodeIndex " + toNodeIndex);
+            ++firstRetainerIndex[toNodeIndex / nodeFieldCount];
+        }
+        for (var i = 0, firstUnusedRetainerSlot = 0; i < nodeCount; i++) {
+            var retainersCount = firstRetainerIndex[i];
+            firstRetainerIndex[i] = firstUnusedRetainerSlot;
+            retainingNodes[firstUnusedRetainerSlot] = retainersCount;
+            firstUnusedRetainerSlot += retainersCount;
+        }
+        firstRetainerIndex[nodeCount] = retainingNodes.length;
+
+        var nextNodeFirstEdgeIndex = firstEdgeIndexes[0];
+        for (var srcNodeOrdinal = 0; srcNodeOrdinal < nodeCount; ++srcNodeOrdinal) {
+            var firstEdgeIndex = nextNodeFirstEdgeIndex;
+            nextNodeFirstEdgeIndex = firstEdgeIndexes[srcNodeOrdinal + 1];
+            var srcNodeIndex = srcNodeOrdinal * nodeFieldCount;
+            for (var edgeIndex = firstEdgeIndex; edgeIndex < nextNodeFirstEdgeIndex; edgeIndex += edgeFieldsCount) {
+                var toNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+                if (toNodeIndex % nodeFieldCount)
+                    throw new Error("Invalid toNodeIndex " + toNodeIndex);
+                var firstRetainerSlotIndex = firstRetainerIndex[toNodeIndex / nodeFieldCount];
+                var nextUnusedRetainerSlotIndex = firstRetainerSlotIndex + (--retainingNodes[firstRetainerSlotIndex]);
+                retainingNodes[nextUnusedRetainerSlotIndex] = srcNodeIndex;
+                retainingEdges[nextUnusedRetainerSlotIndex] = edgeIndex;
+            }
+        }
+    },
+
+    /**
+     * @param {number=} nodeIndex
+     */
+    createNode: function(nodeIndex)
+    {
+        throw new Error("Not implemented");
+    },
+
+    createEdge: function(edges, edgeIndex)
+    {
+        throw new Error("Not implemented");
+    },
+
+    createRetainingEdge: function(retainedNodeIndex, retainerIndex)
+    {
+        throw new Error("Not implemented");
+    },
+
+    dispose: function()
+    {
+        delete this._nodes;
+        delete this._strings;
+        delete this._retainingEdges;
+        delete this._retainingNodes;
+        delete this._firstRetainerIndex;
+        if (this._aggregates) {
+            delete this._aggregates;
+            delete this._aggregatesSortedFlags;
+        }
+        delete this._dominatedNodes;
+        delete this._firstDominatedNodeIndex;
+        delete this._nodeDistances;
+        delete this._dominatorsTree;
+    },
+
+    _allNodes: function()
+    {
+        return new WebInspector.HeapSnapshotNodeIterator(this.rootNode());
+    },
+
+    rootNode: function()
+    {
+        return this.createNode(this._rootNodeIndex);
+    },
+
+    get rootNodeIndex()
+    {
+        return this._rootNodeIndex;
+    },
+
+    get totalSize()
+    {
+        return this.rootNode().retainedSize();
+    },
+
+    _getDominatedIndex: function(nodeIndex)
+    {
+        if (nodeIndex % this._nodeFieldCount)
+            throw new Error("Invalid nodeIndex: " + nodeIndex);
+        return this._firstDominatedNodeIndex[nodeIndex / this._nodeFieldCount];
+    },
+
+    _dominatedNodesOfNode: function(node)
+    {
+        var dominatedIndexFrom = this._getDominatedIndex(node.nodeIndex);
+        var dominatedIndexTo = this._getDominatedIndex(node._nextNodeIndex());
+        return new WebInspector.HeapSnapshotArraySlice(this._dominatedNodes, dominatedIndexFrom, dominatedIndexTo);
+    },
+
+    /**
+     * @param {boolean} sortedIndexes
+     * @param {string} key
+     * @param {string=} filterString
+     */
+    aggregates: function(sortedIndexes, key, filterString)
+    {
+        if (!this._aggregates) {
+            this._aggregates = {};
+            this._aggregatesSortedFlags = {};
+        }
+
+        var aggregatesByClassName = this._aggregates[key];
+        if (aggregatesByClassName) {
+            if (sortedIndexes && !this._aggregatesSortedFlags[key]) {
+                this._sortAggregateIndexes(aggregatesByClassName);
+                this._aggregatesSortedFlags[key] = sortedIndexes;
+            }
+            return aggregatesByClassName;
+        }
+
+        var filter;
+        if (filterString)
+            filter = this._parseFilter(filterString);
+
+        var aggregates = this._buildAggregates(filter);
+        this._calculateClassesRetainedSize(aggregates.aggregatesByClassIndex, filter);
+        aggregatesByClassName = aggregates.aggregatesByClassName;
+
+        if (sortedIndexes)
+            this._sortAggregateIndexes(aggregatesByClassName);
+
+        this._aggregatesSortedFlags[key] = sortedIndexes;
+        this._aggregates[key] = aggregatesByClassName;
+
+        return aggregatesByClassName;
+    },
+
+    aggregatesForDiff: function()
+    {
+        if (this._aggregatesForDiff)
+            return this._aggregatesForDiff;
+
+        var aggregatesByClassName = this.aggregates(true, "allObjects");
+        this._aggregatesForDiff  = {};
+
+        var node = this.createNode();
+        for (var className in aggregatesByClassName) {
+            var aggregate = aggregatesByClassName[className];
+            var indexes = aggregate.idxs;
+            var ids = new Array(indexes.length);
+            var selfSizes = new Array(indexes.length);
+            for (var i = 0; i < indexes.length; i++) {
+                node.nodeIndex = indexes[i];
+                ids[i] = node.id();
+                selfSizes[i] = node.selfSize();
+            }
+
+            this._aggregatesForDiff[className] = {
+                indexes: indexes,
+                ids: ids,
+                selfSizes: selfSizes
+            };
+        }
+        return this._aggregatesForDiff;
+    },
+
+    distanceForUserRoot: function(node)
+    {
+        return 1;
+    },
+
+    _calculateDistances: function()
+    {
+        var nodeFieldCount = this._nodeFieldCount;
+        var distances = new Uint32Array(this.nodeCount);
+
+        // bfs for Window roots
+        var nodesToVisit = new Uint32Array(this.nodeCount);
+        var nodesToVisitLength = 0;
+        for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
+            var node = iter.edge.node();
+            var distance = this.distanceForUserRoot(node);
+            if (distance !== -1) {
+                nodesToVisit[nodesToVisitLength++] = node.nodeIndex;
+                distances[node.nodeIndex / nodeFieldCount] = distance;
+            }
+        }
+        this._bfs(nodesToVisit, nodesToVisitLength, distances);
+
+        // bfs for root
+        nodesToVisitLength = 0;
+        nodesToVisit[nodesToVisitLength++] = this._rootNodeIndex;
+        distances[this._rootNodeIndex / nodeFieldCount] = 1;
+        this._bfs(nodesToVisit, nodesToVisitLength, distances);
+        this._nodeDistances = distances;
+    },
+
+    _bfs: function(nodesToVisit, nodesToVisitLength, distances)
+    {
+        // Peload fields into local variables for better performance.
+        var edgeFieldsCount = this._edgeFieldsCount;
+        var nodeFieldCount = this._nodeFieldCount;
+        var containmentEdges = this._containmentEdges;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
+        var edgeToNodeOffset = this._edgeToNodeOffset;
+        var edgeTypeOffset = this._edgeTypeOffset;
+        var nodes = this._nodes;
+        var nodeCount = this.nodeCount;
+        var containmentEdgesLength = containmentEdges.length;
+        var edgeWeakType = this._edgeWeakType;
+
+        var index = 0;
+        while (index < nodesToVisitLength) {
+            var nodeIndex = nodesToVisit[index++]; // shift generates too much garbage.
+            var nodeOrdinal = nodeIndex / nodeFieldCount;
+            var distance = distances[nodeOrdinal] + 1;
+            var firstEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+            var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1];
+            for (var edgeIndex = firstEdgeIndex; edgeIndex < edgesEnd; edgeIndex += edgeFieldsCount) {
+                var edgeType = containmentEdges[edgeIndex + edgeTypeOffset];
+                if (edgeType == edgeWeakType)
+                    continue;
+                var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+                var childNodeOrdinal = childNodeIndex / nodeFieldCount;
+                if (distances[childNodeOrdinal])
+                    continue;
+                distances[childNodeOrdinal] = distance;
+                nodesToVisit[nodesToVisitLength++] = childNodeIndex;
+            }
+        }
+        if (nodesToVisitLength > nodeCount)
+            throw new Error("BFS failed. Nodes to visit (" + nodesToVisitLength + ") is more than nodes count (" + nodeCount + ")");
+    },
+
+    _buildAggregates: function(filter)
+    {
+        var aggregates = {};
+        var aggregatesByClassName = {};
+        var classIndexes = [];
+        var nodes = this._nodes;
+        var mapAndFlag = this.userObjectsMapAndFlag();
+        var flags = mapAndFlag ? mapAndFlag.map : null;
+        var flag = mapAndFlag ? mapAndFlag.flag : 0;
+        var nodesLength = nodes.length;
+        var nodeNativeType = this._nodeNativeType;
+        var nodeFieldCount = this._nodeFieldCount;
+        var selfSizeOffset = this._nodeSelfSizeOffset;
+        var nodeTypeOffset = this._nodeTypeOffset;
+        var node = this.rootNode();
+        var nodeDistances = this._nodeDistances;
+
+        for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
+            var nodeOrdinal = nodeIndex / nodeFieldCount;
+            if (flags && !(flags[nodeOrdinal] & flag))
+                continue;
+            node.nodeIndex = nodeIndex;
+            if (filter && !filter(node))
+                continue;
+            var selfSize = nodes[nodeIndex + selfSizeOffset];
+            if (!selfSize && nodes[nodeIndex + nodeTypeOffset] !== nodeNativeType)
+                continue;
+            var classIndex = node.classIndex();
+            if (!(classIndex in aggregates)) {
+                var nodeType = node.type();
+                var nameMatters = nodeType === "object" || nodeType === "native";
+                var value = {
+                    count: 1,
+                    distance: nodeDistances[nodeOrdinal],
+                    self: selfSize,
+                    maxRet: 0,
+                    type: nodeType,
+                    name: nameMatters ? node.name() : null,
+                    idxs: [nodeIndex]
+                };
+                aggregates[classIndex] = value;
+                classIndexes.push(classIndex);
+                aggregatesByClassName[node.className()] = value;
+            } else {
+                var clss = aggregates[classIndex];
+                clss.distance = Math.min(clss.distance, nodeDistances[nodeOrdinal]);
+                ++clss.count;
+                clss.self += selfSize;
+                clss.idxs.push(nodeIndex);
+            }
+        }
+
+        // Shave off provisionally allocated space.
+        for (var i = 0, l = classIndexes.length; i < l; ++i) {
+            var classIndex = classIndexes[i];
+            aggregates[classIndex].idxs = aggregates[classIndex].idxs.slice();
+        }
+        return {aggregatesByClassName: aggregatesByClassName, aggregatesByClassIndex: aggregates};
+    },
+
+    _calculateClassesRetainedSize: function(aggregates, filter)
+    {
+        var rootNodeIndex = this._rootNodeIndex;
+        var node = this.createNode(rootNodeIndex);
+        var list = [rootNodeIndex];
+        var sizes = [-1];
+        var classes = [];
+        var seenClassNameIndexes = {};
+        var nodeFieldCount = this._nodeFieldCount;
+        var nodeTypeOffset = this._nodeTypeOffset;
+        var nodeNativeType = this._nodeNativeType;
+        var dominatedNodes = this._dominatedNodes;
+        var nodes = this._nodes;
+        var mapAndFlag = this.userObjectsMapAndFlag();
+        var flags = mapAndFlag ? mapAndFlag.map : null;
+        var flag = mapAndFlag ? mapAndFlag.flag : 0;
+        var firstDominatedNodeIndex = this._firstDominatedNodeIndex;
+
+        while (list.length) {
+            var nodeIndex = list.pop();
+            node.nodeIndex = nodeIndex;
+            var classIndex = node.classIndex();
+            var seen = !!seenClassNameIndexes[classIndex];
+            var nodeOrdinal = nodeIndex / nodeFieldCount;
+            var dominatedIndexFrom = firstDominatedNodeIndex[nodeOrdinal];
+            var dominatedIndexTo = firstDominatedNodeIndex[nodeOrdinal + 1];
+
+            if (!seen &&
+                (!flags || (flags[nodeOrdinal] & flag)) &&
+                (!filter || filter(node)) &&
+                (node.selfSize() || nodes[nodeIndex + nodeTypeOffset] === nodeNativeType)
+               ) {
+                aggregates[classIndex].maxRet += node.retainedSize();
+                if (dominatedIndexFrom !== dominatedIndexTo) {
+                    seenClassNameIndexes[classIndex] = true;
+                    sizes.push(list.length);
+                    classes.push(classIndex);
+                }
+            }
+            for (var i = dominatedIndexFrom; i < dominatedIndexTo; i++)
+                list.push(dominatedNodes[i]);
+
+            var l = list.length;
+            while (sizes[sizes.length - 1] === l) {
+                sizes.pop();
+                classIndex = classes.pop();
+                seenClassNameIndexes[classIndex] = false;
+            }
+        }
+    },
+
+    _sortAggregateIndexes: function(aggregates)
+    {
+        var nodeA = this.createNode();
+        var nodeB = this.createNode();
+        for (var clss in aggregates)
+            aggregates[clss].idxs.sort(
+                function(idxA, idxB) {
+                    nodeA.nodeIndex = idxA;
+                    nodeB.nodeIndex = idxB;
+                    return nodeA.id() < nodeB.id() ? -1 : 1;
+                });
+    },
+
+    _buildPostOrderIndex: function()
+    {
+        var nodeFieldCount = this._nodeFieldCount;
+        var nodes = this._nodes;
+        var nodeCount = this.nodeCount;
+        var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+
+        var edgeFieldsCount = this._edgeFieldsCount;
+        var edgeTypeOffset = this._edgeTypeOffset;
+        var edgeToNodeOffset = this._edgeToNodeOffset;
+        var edgeShortcutType = this._edgeShortcutType;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
+        var containmentEdges = this._containmentEdges;
+        var containmentEdgesLength = this._containmentEdges.length;
+
+        var mapAndFlag = this.userObjectsMapAndFlag();
+        var flags = mapAndFlag ? mapAndFlag.map : null;
+        var flag = mapAndFlag ? mapAndFlag.flag : 0;
+
+        var nodesToVisit = new Uint32Array(nodeCount);
+        var postOrderIndex2NodeOrdinal = new Uint32Array(nodeCount);
+        var nodeOrdinal2PostOrderIndex = new Uint32Array(nodeCount);
+        var painted = new Uint8Array(nodeCount);
+        var nodesToVisitLength = 0;
+        var postOrderIndex = 0;
+        var grey = 1;
+        var black = 2;
+
+        nodesToVisit[nodesToVisitLength++] = rootNodeOrdinal;
+        painted[rootNodeOrdinal] = grey;
+
+        while (nodesToVisitLength) {
+            var nodeOrdinal = nodesToVisit[nodesToVisitLength - 1];
+
+            if (painted[nodeOrdinal] === grey) {
+                painted[nodeOrdinal] = black;
+                var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
+                var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+                var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
+                for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
+                    if (nodeOrdinal !== rootNodeOrdinal && containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType)
+                        continue;
+                    var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+                    var childNodeOrdinal = childNodeIndex / nodeFieldCount;
+                    var childNodeFlag = !flags || (flags[childNodeOrdinal] & flag);
+                    // We are skipping the edges from non-page-owned nodes to page-owned nodes.
+                    // Otherwise the dominators for the objects that also were retained by debugger would be affected.
+                    if (nodeOrdinal !== rootNodeOrdinal && childNodeFlag && !nodeFlag)
+                        continue;
+                    if (!painted[childNodeOrdinal]) {
+                        painted[childNodeOrdinal] = grey;
+                        nodesToVisit[nodesToVisitLength++] = childNodeOrdinal;
+                    }
+                }
+            } else {
+                nodeOrdinal2PostOrderIndex[nodeOrdinal] = postOrderIndex;
+                postOrderIndex2NodeOrdinal[postOrderIndex++] = nodeOrdinal;
+                --nodesToVisitLength;
+            }
+        }
+
+        if (postOrderIndex !== nodeCount) {
+            var dumpNode = this.rootNode();
+            for (var i = 0; i < nodeCount; ++i) {
+                if (painted[i] !== black) {
+                    dumpNode.nodeIndex = i * nodeFieldCount;
+                    console.log(JSON.stringify(dumpNode.serialize()));
+                    var retainers = dumpNode.retainers();
+                    while (retainers) {
+                        console.log("edgeName: " + retainers.item().name() + " nodeClassName: " + retainers.item().node().className());
+                        retainers = retainers.item().node().retainers();
+                    }
+                }
+            }
+            throw new Error("Postordering failed. " + (nodeCount - postOrderIndex) + " hanging nodes");
+        }
+
+        return {postOrderIndex2NodeOrdinal: postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex: nodeOrdinal2PostOrderIndex};
+    },
+
+    // The algorithm is based on the article:
+    // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
+    // Softw. Pract. Exper. 4 (2001), pp. 1-10.
+    /**
+     * @param {Array.<number>} postOrderIndex2NodeOrdinal
+     * @param {Array.<number>} nodeOrdinal2PostOrderIndex
+     */
+    _buildDominatorTree: function(postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex)
+    {
+        var nodeFieldCount = this._nodeFieldCount;
+        var nodes = this._nodes;
+        var firstRetainerIndex = this._firstRetainerIndex;
+        var retainingNodes = this._retainingNodes;
+        var retainingEdges = this._retainingEdges;
+        var edgeFieldsCount = this._edgeFieldsCount;
+        var edgeTypeOffset = this._edgeTypeOffset;
+        var edgeToNodeOffset = this._edgeToNodeOffset;
+        var edgeShortcutType = this._edgeShortcutType;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
+        var containmentEdges = this._containmentEdges;
+        var containmentEdgesLength = this._containmentEdges.length;
+        var rootNodeIndex = this._rootNodeIndex;
+
+        var mapAndFlag = this.userObjectsMapAndFlag();
+        var flags = mapAndFlag ? mapAndFlag.map : null;
+        var flag = mapAndFlag ? mapAndFlag.flag : 0;
+
+        var nodesCount = postOrderIndex2NodeOrdinal.length;
+        var rootPostOrderedIndex = nodesCount - 1;
+        var noEntry = nodesCount;
+        var dominators = new Uint32Array(nodesCount);
+        for (var i = 0; i < rootPostOrderedIndex; ++i)
+            dominators[i] = noEntry;
+        dominators[rootPostOrderedIndex] = rootPostOrderedIndex;
+
+        // The affected array is used to mark entries which dominators
+        // have to be racalculated because of changes in their retainers.
+        var affected = new Uint8Array(nodesCount);
+        var nodeOrdinal;
+
+        { // Mark the root direct children as affected.
+            nodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+            var beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
+            var endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
+            for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
+                 toNodeFieldIndex < endEdgeToNodeFieldIndex;
+                 toNodeFieldIndex += edgeFieldsCount) {
+                var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
+                affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
+            }
+        }
+
+        var changed = true;
+        while (changed) {
+            changed = false;
+            for (var postOrderIndex = rootPostOrderedIndex - 1; postOrderIndex >= 0; --postOrderIndex) {
+                if (affected[postOrderIndex] === 0)
+                    continue;
+                affected[postOrderIndex] = 0;
+                // If dominator of the entry has already been set to root,
+                // then it can't propagate any further.
+                if (dominators[postOrderIndex] === rootPostOrderedIndex)
+                    continue;
+                nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
+                var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
+                var newDominatorIndex = noEntry;
+                var beginRetainerIndex = firstRetainerIndex[nodeOrdinal];
+                var endRetainerIndex = firstRetainerIndex[nodeOrdinal + 1];
+                for (var retainerIndex = beginRetainerIndex; retainerIndex < endRetainerIndex; ++retainerIndex) {
+                    var retainerEdgeIndex = retainingEdges[retainerIndex];
+                    var retainerEdgeType = containmentEdges[retainerEdgeIndex + edgeTypeOffset];
+                    var retainerNodeIndex = retainingNodes[retainerIndex];
+                    if (retainerNodeIndex !== rootNodeIndex && retainerEdgeType === edgeShortcutType)
+                        continue;
+                    var retainerNodeOrdinal = retainerNodeIndex / nodeFieldCount;
+                    var retainerNodeFlag = !flags || (flags[retainerNodeOrdinal] & flag);
+                    // We are skipping the edges from non-page-owned nodes to page-owned nodes.
+                    // Otherwise the dominators for the objects that also were retained by debugger would be affected.
+                    if (retainerNodeIndex !== rootNodeIndex && nodeFlag && !retainerNodeFlag)
+                        continue;
+                    var retanerPostOrderIndex = nodeOrdinal2PostOrderIndex[retainerNodeOrdinal];
+                    if (dominators[retanerPostOrderIndex] !== noEntry) {
+                        if (newDominatorIndex === noEntry)
+                            newDominatorIndex = retanerPostOrderIndex;
+                        else {
+                            while (retanerPostOrderIndex !== newDominatorIndex) {
+                                while (retanerPostOrderIndex < newDominatorIndex)
+                                    retanerPostOrderIndex = dominators[retanerPostOrderIndex];
+                                while (newDominatorIndex < retanerPostOrderIndex)
+                                    newDominatorIndex = dominators[newDominatorIndex];
+                            }
+                        }
+                        // If idom has already reached the root, it doesn't make sense
+                        // to check other retainers.
+                        if (newDominatorIndex === rootPostOrderedIndex)
+                            break;
+                    }
+                }
+                if (newDominatorIndex !== noEntry && dominators[postOrderIndex] !== newDominatorIndex) {
+                    dominators[postOrderIndex] = newDominatorIndex;
+                    changed = true;
+                    nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
+                    beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
+                    endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
+                    for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
+                         toNodeFieldIndex < endEdgeToNodeFieldIndex;
+                         toNodeFieldIndex += edgeFieldsCount) {
+                        var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
+                        affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
+                    }
+                }
+            }
+        }
+
+        var dominatorsTree = new Uint32Array(nodesCount);
+        for (var postOrderIndex = 0, l = dominators.length; postOrderIndex < l; ++postOrderIndex) {
+            nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
+            dominatorsTree[nodeOrdinal] = postOrderIndex2NodeOrdinal[dominators[postOrderIndex]];
+        }
+        return dominatorsTree;
+    },
+
+    _calculateRetainedSizes: function(postOrderIndex2NodeOrdinal)
+    {
+        var nodeCount = this.nodeCount;
+        var nodes = this._nodes;
+        var nodeSelfSizeOffset = this._nodeSelfSizeOffset;
+        var nodeFieldCount = this._nodeFieldCount;
+        var dominatorsTree = this._dominatorsTree;
+        // Reuse now unused edge_count field to store retained size.
+        var nodeRetainedSizeOffset = this._nodeRetainedSizeOffset = this._nodeEdgeCountOffset;
+        delete this._nodeEdgeCountOffset;
+
+        for (var nodeIndex = 0, l = nodes.length; nodeIndex < l; nodeIndex += nodeFieldCount)
+            nodes[nodeIndex + nodeRetainedSizeOffset] = nodes[nodeIndex + nodeSelfSizeOffset];
+
+        // Propagate retained sizes for each node excluding root.
+        for (var postOrderIndex = 0; postOrderIndex < nodeCount - 1; ++postOrderIndex) {
+            var nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
+            var nodeIndex = nodeOrdinal * nodeFieldCount;
+            var dominatorIndex = dominatorsTree[nodeOrdinal] * nodeFieldCount;
+            nodes[dominatorIndex + nodeRetainedSizeOffset] += nodes[nodeIndex + nodeRetainedSizeOffset];
+        }
+    },
+
+    _buildDominatedNodes: function()
+    {
+        // Builds up two arrays:
+        //  - "dominatedNodes" is a continuous array, where each node owns an
+        //    interval (can be empty) with corresponding dominated nodes.
+        //  - "indexArray" is an array of indexes in the "dominatedNodes"
+        //    with the same positions as in the _nodeIndex.
+        var indexArray = this._firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1);
+        // All nodes except the root have dominators.
+        var dominatedNodes = this._dominatedNodes = new Uint32Array(this.nodeCount - 1);
+
+        // Count the number of dominated nodes for each node. Skip the root (node at
+        // index 0) as it is the only node that dominates itself.
+        var nodeFieldCount = this._nodeFieldCount;
+        var dominatorsTree = this._dominatorsTree;
+
+        var fromNodeOrdinal = 0;
+        var toNodeOrdinal = this.nodeCount;
+        var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+        if (rootNodeOrdinal === fromNodeOrdinal)
+            fromNodeOrdinal = 1;
+        else if (rootNodeOrdinal === toNodeOrdinal - 1)
+            toNodeOrdinal = toNodeOrdinal - 1;
+        else
+            throw new Error("Root node is expected to be either first or last");
+        for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal)
+            ++indexArray[dominatorsTree[nodeOrdinal]];
+        // Put in the first slot of each dominatedNodes slice the count of entries
+        // that will be filled.
+        var firstDominatedNodeIndex = 0;
+        for (var i = 0, l = this.nodeCount; i < l; ++i) {
+            var dominatedCount = dominatedNodes[firstDominatedNodeIndex] = indexArray[i];
+            indexArray[i] = firstDominatedNodeIndex;
+            firstDominatedNodeIndex += dominatedCount;
+        }
+        indexArray[this.nodeCount] = dominatedNodes.length;
+        // Fill up the dominatedNodes array with indexes of dominated nodes. Skip the root (node at
+        // index 0) as it is the only node that dominates itself.
+        for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal) {
+            var dominatorOrdinal = dominatorsTree[nodeOrdinal];
+            var dominatedRefIndex = indexArray[dominatorOrdinal];
+            dominatedRefIndex += (--dominatedNodes[dominatedRefIndex]);
+            dominatedNodes[dominatedRefIndex] = nodeOrdinal * nodeFieldCount;
+        }
+    },
+
+    _markInvisibleEdges: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    _numbersComparator: function(a, b)
+    {
+        return a < b ? -1 : (a > b ? 1 : 0);
+    },
+
+    _calculateFlags: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    userObjectsMapAndFlag: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates)
+    {
+        var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
+        if (snapshotDiff)
+            return snapshotDiff;
+        snapshotDiff = {};
+
+        var aggregates = this.aggregates(true, "allObjects");
+        for (var className in baseSnapshotAggregates) {
+            var baseAggregate = baseSnapshotAggregates[className];
+            var diff = this._calculateDiffForClass(baseAggregate, aggregates[className]);
+            if (diff)
+                snapshotDiff[className] = diff;
+        }
+        var emptyBaseAggregate = { ids: [], indexes: [], selfSizes: [] };
+        for (var className in aggregates) {
+            if (className in baseSnapshotAggregates)
+                continue;
+            snapshotDiff[className] = this._calculateDiffForClass(emptyBaseAggregate, aggregates[className]);
+        }
+
+        this._snapshotDiffs[baseSnapshotId] = snapshotDiff;
+        return snapshotDiff;
+    },
+
+    _calculateDiffForClass: function(baseAggregate, aggregate)
+    {
+        var baseIds = baseAggregate.ids;
+        var baseIndexes = baseAggregate.indexes;
+        var baseSelfSizes = baseAggregate.selfSizes;
+
+        var indexes = aggregate ? aggregate.idxs : [];
+
+        var i = 0, l = baseIds.length;
+        var j = 0, m = indexes.length;
+        var diff = { addedCount: 0,
+                     removedCount: 0,
+                     addedSize: 0,
+                     removedSize: 0,
+                     deletedIndexes: [],
+                     addedIndexes: [] };
+
+        var nodeB = this.createNode(indexes[j]);
+        while (i < l && j < m) {
+            var nodeAId = baseIds[i];
+            if (nodeAId < nodeB.id()) {
+                diff.deletedIndexes.push(baseIndexes[i]);
+                diff.removedCount++;
+                diff.removedSize += baseSelfSizes[i];
+                ++i;
+            } else if (nodeAId > nodeB.id()) { // Native nodes(e.g. dom groups) may have ids less than max JS object id in the base snapshot
+                diff.addedIndexes.push(indexes[j]);
+                diff.addedCount++;
+                diff.addedSize += nodeB.selfSize();
+                nodeB.nodeIndex = indexes[++j];
+            } else { // nodeAId === nodeB.id()
+                ++i;
+                nodeB.nodeIndex = indexes[++j];
+            }
+        }
+        while (i < l) {
+            diff.deletedIndexes.push(baseIndexes[i]);
+            diff.removedCount++;
+            diff.removedSize += baseSelfSizes[i];
+            ++i;
+        }
+        while (j < m) {
+            diff.addedIndexes.push(indexes[j]);
+            diff.addedCount++;
+            diff.addedSize += nodeB.selfSize();
+            nodeB.nodeIndex = indexes[++j];
+        }
+        diff.countDelta = diff.addedCount - diff.removedCount;
+        diff.sizeDelta = diff.addedSize - diff.removedSize;
+        if (!diff.addedCount && !diff.removedCount)
+            return null;
+        return diff;
+    },
+
+    _nodeForSnapshotObjectId: function(snapshotObjectId)
+    {
+        for (var it = this._allNodes(); it.hasNext(); it.next()) {
+            if (it.node.id() === snapshotObjectId)
+                return it.node;
+        }
+        return null;
+    },
+
+    nodeClassName: function(snapshotObjectId)
+    {
+        var node = this._nodeForSnapshotObjectId(snapshotObjectId);
+        if (node)
+            return node.className();
+        return null;
+    },
+
+    dominatorIdsForNode: function(snapshotObjectId)
+    {
+        var node = this._nodeForSnapshotObjectId(snapshotObjectId);
+        if (!node)
+            return null;
+        var result = [];
+        while (!node.isRoot()) {
+            result.push(node.id());
+            node.nodeIndex = node.dominatorIndex();
+        }
+        return result;
+    },
+
+    _parseFilter: function(filter)
+    {
+        if (!filter)
+            return null;
+        var parsedFilter = eval("(function(){return " + filter + "})()");
+        return parsedFilter.bind(this);
+    },
+
+    createEdgesProvider: function(nodeIndex, showHiddenData)
+    {
+        var node = this.createNode(nodeIndex);
+        var filter = this.containmentEdgesFilter(showHiddenData);
+        return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges());
+    },
+
+    createEdgesProviderForTest: function(nodeIndex, filter)
+    {
+        var node = this.createNode(nodeIndex);
+        return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges());
+    },
+
+    retainingEdgesFilter: function(showHiddenData)
+    {
+        return null;
+    },
+
+    containmentEdgesFilter: function(showHiddenData)
+    {
+        return null;
+    },
+
+    createRetainingEdgesProvider: function(nodeIndex, showHiddenData)
+    {
+        var node = this.createNode(nodeIndex);
+        var filter = this.retainingEdgesFilter(showHiddenData);
+        return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.retainers());
+    },
+
+    createAddedNodesProvider: function(baseSnapshotId, className)
+    {
+        var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
+        var diffForClass = snapshotDiff[className];
+        return new WebInspector.HeapSnapshotNodesProvider(this, null, diffForClass.addedIndexes);
+    },
+
+    createDeletedNodesProvider: function(nodeIndexes)
+    {
+        return new WebInspector.HeapSnapshotNodesProvider(this, null, nodeIndexes);
+    },
+
+    classNodesFilter: function()
+    {
+        return null;
+    },
+
+    createNodesProviderForClass: function(className, aggregatesKey)
+    {
+        return new WebInspector.HeapSnapshotNodesProvider(this, this.classNodesFilter(), this.aggregates(false, aggregatesKey)[className].idxs);
+    },
+
+    createNodesProviderForDominator: function(nodeIndex)
+    {
+        var node = this.createNode(nodeIndex);
+        return new WebInspector.HeapSnapshotNodesProvider(this, null, this._dominatedNodesOfNode(node));
+    },
+
+    updateStaticData: function()
+    {
+        return {nodeCount: this.nodeCount, rootNodeIndex: this._rootNodeIndex, totalSize: this.totalSize, uid: this.uid};
+    }
+};
+
+/**
+ * @constructor
+ * @param {Array.<number>=} unfilteredIterationOrder
+ */
+WebInspector.HeapSnapshotFilteredOrderedIterator = function(iterator, filter, unfilteredIterationOrder)
+{
+    this._filter = filter;
+    this._iterator = iterator;
+    this._unfilteredIterationOrder = unfilteredIterationOrder;
+    this._iterationOrder = null;
+    this._position = 0;
+    this._currentComparator = null;
+    this._sortedPrefixLength = 0;
+}
+
+WebInspector.HeapSnapshotFilteredOrderedIterator.prototype = {
+    _createIterationOrder: function()
+    {
+        if (this._iterationOrder)
+            return;
+        if (this._unfilteredIterationOrder && !this._filter) {
+            this._iterationOrder = this._unfilteredIterationOrder.slice(0);
+            this._unfilteredIterationOrder = null;
+            return;
+        }
+        this._iterationOrder = [];
+        var iterator = this._iterator;
+        if (!this._unfilteredIterationOrder && !this._filter) {
+            for (iterator.rewind(); iterator.hasNext(); iterator.next())
+                this._iterationOrder.push(iterator.index());
+        } else if (!this._unfilteredIterationOrder) {
+            for (iterator.rewind(); iterator.hasNext(); iterator.next()) {
+                if (this._filter(iterator.item()))
+                    this._iterationOrder.push(iterator.index());
+            }
+        } else {
+            var order = this._unfilteredIterationOrder.constructor === Array ?
+                this._unfilteredIterationOrder : this._unfilteredIterationOrder.slice(0);
+            for (var i = 0, l = order.length; i < l; ++i) {
+                iterator.setIndex(order[i]);
+                if (this._filter(iterator.item()))
+                    this._iterationOrder.push(iterator.index());
+            }
+            this._unfilteredIterationOrder = null;
+        }
+    },
+
+    rewind: function()
+    {
+        this._position = 0;
+    },
+
+    hasNext: function()
+    {
+        return this._position < this._iterationOrder.length;
+    },
+
+    isEmpty: function()
+    {
+        if (this._iterationOrder)
+            return !this._iterationOrder.length;
+        if (this._unfilteredIterationOrder && !this._filter)
+            return !this._unfilteredIterationOrder.length;
+        var iterator = this._iterator;
+        if (!this._unfilteredIterationOrder && !this._filter) {
+            iterator.rewind();
+            return !iterator.hasNext();
+        } else if (!this._unfilteredIterationOrder) {
+            for (iterator.rewind(); iterator.hasNext(); iterator.next())
+                if (this._filter(iterator.item()))
+                    return false;
+        } else {
+            var order = this._unfilteredIterationOrder.constructor === Array ?
+                this._unfilteredIterationOrder : this._unfilteredIterationOrder.slice(0);
+            for (var i = 0, l = order.length; i < l; ++i) {
+                iterator.setIndex(order[i]);
+                if (this._filter(iterator.item()))
+                    return false;
+            }
+        }
+        return true;
+    },
+
+    item: function()
+    {
+        this._iterator.setIndex(this._iterationOrder[this._position]);
+        return this._iterator.item();
+    },
+
+    get length()
+    {
+        this._createIterationOrder();
+        return this._iterationOrder.length;
+    },
+
+    next: function()
+    {
+        ++this._position;
+    },
+
+    /**
+     * @param {number} begin
+     * @param {number} end
+     */
+    serializeItemsRange: function(begin, end)
+    {
+        this._createIterationOrder();
+        if (begin > end)
+            throw new Error("Start position > end position: " + begin + " > " + end);
+        if (end >= this._iterationOrder.length)
+            end = this._iterationOrder.length;
+        if (this._sortedPrefixLength < end) {
+            this.sort(this._currentComparator, this._sortedPrefixLength, this._iterationOrder.length - 1, end - this._sortedPrefixLength);
+            this._sortedPrefixLength = end;
+        }
+
+        this._position = begin;
+        var startPosition = this._position;
+        var count = end - begin;
+        var result = new Array(count);
+        for (var i = 0 ; i < count && this.hasNext(); ++i, this.next())
+            result[i] = this.item().serialize();
+        result.length = i;
+        result.totalLength = this._iterationOrder.length;
+
+        result.startPosition = startPosition;
+        result.endPosition = this._position;
+        return result;
+    },
+
+    sortAll: function()
+    {
+        this._createIterationOrder();
+        if (this._sortedPrefixLength === this._iterationOrder.length)
+            return;
+        this.sort(this._currentComparator, this._sortedPrefixLength, this._iterationOrder.length - 1, this._iterationOrder.length);
+        this._sortedPrefixLength = this._iterationOrder.length;
+    },
+
+    sortAndRewind: function(comparator)
+    {
+        this._currentComparator = comparator;
+        this._sortedPrefixLength = 0;
+        this.rewind();
+    }
+}
+
+WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator = function(fieldNames)
+{
+    return {fieldName1: fieldNames[0], ascending1: fieldNames[1], fieldName2: fieldNames[2], ascending2: fieldNames[3]};
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotFilteredOrderedIterator}
+ */
+WebInspector.HeapSnapshotEdgesProvider = function(snapshot, filter, edgesIter)
+{
+    this.snapshot = snapshot;
+    WebInspector.HeapSnapshotFilteredOrderedIterator.call(this, edgesIter, filter);
+}
+
+WebInspector.HeapSnapshotEdgesProvider.prototype = {
+    sort: function(comparator, leftBound, rightBound, count)
+    {
+        var fieldName1 = comparator.fieldName1;
+        var fieldName2 = comparator.fieldName2;
+        var ascending1 = comparator.ascending1;
+        var ascending2 = comparator.ascending2;
+
+        var edgeA = this._iterator.item().clone();
+        var edgeB = edgeA.clone();
+        var nodeA = this.snapshot.createNode();
+        var nodeB = this.snapshot.createNode();
+
+        function compareEdgeFieldName(ascending, indexA, indexB)
+        {
+            edgeA.edgeIndex = indexA;
+            edgeB.edgeIndex = indexB;
+            if (edgeB.name() === "__proto__") return -1;
+            if (edgeA.name() === "__proto__") return 1;
+            var result =
+                edgeA.hasStringName() === edgeB.hasStringName() ?
+                (edgeA.name() < edgeB.name() ? -1 : (edgeA.name() > edgeB.name() ? 1 : 0)) :
+                (edgeA.hasStringName() ? -1 : 1);
+            return ascending ? result : -result;
+        }
+
+        function compareNodeField(fieldName, ascending, indexA, indexB)
+        {
+            edgeA.edgeIndex = indexA;
+            nodeA.nodeIndex = edgeA.nodeIndex();
+            var valueA = nodeA[fieldName]();
+
+            edgeB.edgeIndex = indexB;
+            nodeB.nodeIndex = edgeB.nodeIndex();
+            var valueB = nodeB[fieldName]();
+
+            var result = valueA < valueB ? -1 : (valueA > valueB ? 1 : 0);
+            return ascending ? result : -result;
+        }
+
+        function compareEdgeAndNode(indexA, indexB) {
+            var result = compareEdgeFieldName(ascending1, indexA, indexB);
+            if (result === 0)
+                result = compareNodeField(fieldName2, ascending2, indexA, indexB);
+            return result;
+        }
+
+        function compareNodeAndEdge(indexA, indexB) {
+            var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
+            if (result === 0)
+                result = compareEdgeFieldName(ascending2, indexA, indexB);
+            return result;
+        }
+
+        function compareNodeAndNode(indexA, indexB) {
+            var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
+            if (result === 0)
+                result = compareNodeField(fieldName2, ascending2, indexA, indexB);
+            return result;
+        }
+
+        if (fieldName1 === "!edgeName")
+            this._iterationOrder.sortRange(compareEdgeAndNode, leftBound, rightBound, count);
+        else if (fieldName2 === "!edgeName")
+            this._iterationOrder.sortRange(compareNodeAndEdge, leftBound, rightBound, count);
+        else
+            this._iterationOrder.sortRange(compareNodeAndNode, leftBound, rightBound, count);
+    },
+
+    __proto__: WebInspector.HeapSnapshotFilteredOrderedIterator.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotFilteredOrderedIterator}
+ * @param {Array.<number>=} nodeIndexes
+ */
+WebInspector.HeapSnapshotNodesProvider = function(snapshot, filter, nodeIndexes)
+{
+    this.snapshot = snapshot;
+    WebInspector.HeapSnapshotFilteredOrderedIterator.call(this, snapshot._allNodes(), filter, nodeIndexes);
+}
+
+WebInspector.HeapSnapshotNodesProvider.prototype = {
+    nodePosition: function(snapshotObjectId)
+    {
+        this._createIterationOrder();
+        if (this.isEmpty())
+            return -1;
+        this.sortAll();
+
+        var node = this.snapshot.createNode();
+        for (var i = 0; i < this._iterationOrder.length; i++) {
+            node.nodeIndex = this._iterationOrder[i];
+            if (node.id() === snapshotObjectId)
+                return i;
+        }
+        return -1;
+    },
+
+    sort: function(comparator, leftBound, rightBound, count)
+    {
+        var fieldName1 = comparator.fieldName1;
+        var fieldName2 = comparator.fieldName2;
+        var ascending1 = comparator.ascending1;
+        var ascending2 = comparator.ascending2;
+
+        var nodeA = this.snapshot.createNode();
+        var nodeB = this.snapshot.createNode();
+
+        function sortByNodeField(fieldName, ascending)
+        {
+            var valueOrFunctionA = nodeA[fieldName];
+            var valueA = typeof valueOrFunctionA !== "function" ? valueOrFunctionA : valueOrFunctionA.call(nodeA);
+            var valueOrFunctionB = nodeB[fieldName];
+            var valueB = typeof valueOrFunctionB !== "function" ? valueOrFunctionB : valueOrFunctionB.call(nodeB);
+            var result = valueA < valueB ? -1 : (valueA > valueB ? 1 : 0);
+            return ascending ? result : -result;
+        }
+
+        function sortByComparator(indexA, indexB) {
+            nodeA.nodeIndex = indexA;
+            nodeB.nodeIndex = indexB;
+            var result = sortByNodeField(fieldName1, ascending1);
+            if (result === 0)
+                result = sortByNodeField(fieldName2, ascending2);
+            return result;
+        }
+
+        this._iterationOrder.sortRange(sortByComparator, leftBound, rightBound, count);
+    },
+
+    __proto__: WebInspector.HeapSnapshotFilteredOrderedIterator.prototype
+}
+
diff --git a/Source/devtools/front_end/HeapSnapshotDataGrids.js b/Source/devtools/front_end/HeapSnapshotDataGrids.js
new file mode 100644
index 0000000..7d69be9
--- /dev/null
+++ b/Source/devtools/front_end/HeapSnapshotDataGrids.js
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGrid}
+ */
+WebInspector.HeapSnapshotSortableDataGrid = function(columns)
+{
+    WebInspector.DataGrid.call(this, columns);
+
+    /**
+     * @type {number}
+     */
+    this._recursiveSortingDepth = 0;
+    /**
+     * @type {WebInspector.HeapSnapshotGridNode}
+     */
+    this._highlightedNode = null;
+    /**
+     * @type {boolean}
+     */
+    this._populatedAndSorted = false;
+    this.addEventListener("sorting complete", this._sortingComplete, this);
+    this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this.sortingChanged, this);
+}
+
+WebInspector.HeapSnapshotSortableDataGrid.Events = {
+    ContentShown: "ContentShown"
+}
+
+WebInspector.HeapSnapshotSortableDataGrid.prototype = {
+    /**
+     * @return {number}
+     */
+    defaultPopulateCount: function()
+    {
+        return 100;
+    },
+
+    dispose: function()
+    {
+        var children = this.topLevelNodes();
+        for (var i = 0, l = children.length; i < l; ++i)
+            children[i].dispose();
+    },
+
+    /**
+     * @override
+     */
+    wasShown: function()
+    {
+        if (this._populatedAndSorted)
+            this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this);
+    },
+
+    _sortingComplete: function()
+    {
+        this.removeEventListener("sorting complete", this._sortingComplete, this);
+        this._populatedAndSorted = true;
+        this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this);
+    },
+
+    /**
+     * @override
+     */
+    willHide: function()
+    {
+        this._clearCurrentHighlight();
+    },
+
+    /**
+     * @param {WebInspector.ProfilesPanel} profilesPanel
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Event} event
+     */
+    populateContextMenu: function(profilesPanel, contextMenu, event)
+    {
+        var td = event.target.enclosingNodeOrSelfWithNodeName("td");
+        if (!td)
+            return;
+        var node = td.heapSnapshotNode;
+        if (node instanceof WebInspector.HeapSnapshotInstanceNode || node instanceof WebInspector.HeapSnapshotObjectNode) {
+            function revealInDominatorsView()
+            {
+                profilesPanel.showObject(node.snapshotNodeId, "Dominators");
+            }
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this));
+        } else if (node instanceof WebInspector.HeapSnapshotDominatorObjectNode) {
+            function revealInSummaryView()
+            {
+                profilesPanel.showObject(node.snapshotNodeId, "Summary");
+            }
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this));
+        }
+    },
+
+    resetSortingCache: function()
+    {
+        delete this._lastSortColumnIdentifier;
+        delete this._lastSortAscending;
+    },
+
+    topLevelNodes: function()
+    {
+        return this.rootNode().children;
+    },
+
+    /**
+     * @param {HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId
+     */
+    highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId)
+    {
+    },
+
+    /**
+     * @param {WebInspector.HeapSnapshotGridNode} node
+     */
+    highlightNode: function(node)
+    {
+        var prevNode = this._highlightedNode;
+        this._clearCurrentHighlight();
+        this._highlightedNode = node;
+        this._highlightedNode.element.addStyleClass("highlighted-row");
+        // If highlighted node hasn't changed reinsert it to make the highlight animation restart.
+        if (node === prevNode) {
+            var element = node.element;
+            var parent = element.parentElement;
+            var nextSibling = element.nextSibling;
+            parent.removeChild(element);
+            parent.insertBefore(element, nextSibling);
+        }
+    },
+
+    nodeWasDetached: function(node)
+    {
+        if (this._highlightedNode === node)
+            this._clearCurrentHighlight();
+    },
+
+    _clearCurrentHighlight: function()
+    {
+        if (!this._highlightedNode)
+            return
+        this._highlightedNode.element.removeStyleClass("highlighted-row");
+        this._highlightedNode = null;
+    },
+
+    changeNameFilter: function(filter)
+    {
+        filter = filter.toLowerCase();
+        var children = this.topLevelNodes();
+        for (var i = 0, l = children.length; i < l; ++i) {
+            var node = children[i];
+            if (node.depth === 0)
+                node.revealed = node._name.toLowerCase().indexOf(filter) !== -1;
+        }
+        this.updateVisibleNodes();
+    },
+
+    sortingChanged: function()
+    {
+        var sortAscending = this.isSortOrderAscending();
+        var sortColumnIdentifier = this.sortColumnIdentifier();
+        if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending)
+            return;
+        this._lastSortColumnIdentifier = sortColumnIdentifier;
+        this._lastSortAscending = sortAscending;
+        var sortFields = this._sortFields(sortColumnIdentifier, sortAscending);
+
+        function SortByTwoFields(nodeA, nodeB)
+        {
+            var field1 = nodeA[sortFields[0]];
+            var field2 = nodeB[sortFields[0]];
+            var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
+            if (!sortFields[1])
+                result = -result;
+            if (result !== 0)
+                return result;
+            field1 = nodeA[sortFields[2]];
+            field2 = nodeB[sortFields[2]];
+            result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
+            if (!sortFields[3])
+                result = -result;
+            return result;
+        }
+        this._performSorting(SortByTwoFields);
+    },
+
+    _performSorting: function(sortFunction)
+    {
+        this.recursiveSortingEnter();
+        var children = this._topLevelNodes;
+        this.rootNode().removeChildren();
+        children.sort(sortFunction);
+        for (var i = 0, l = children.length; i < l; ++i) {
+            var child = children[i];
+            this.appendChildAfterSorting(child);
+            if (child.expanded)
+                child.sort();
+        }
+        this.updateVisibleNodes();
+        this.recursiveSortingLeave();
+    },
+
+    appendChildAfterSorting: function(child)
+    {
+        var revealed = child.revealed;
+        this.rootNode().appendChild(child);
+        child.revealed = revealed;
+    },
+
+    updateVisibleNodes: function()
+    {
+    },
+
+    recursiveSortingEnter: function()
+    {
+        ++this._recursiveSortingDepth;
+    },
+
+    recursiveSortingLeave: function()
+    {
+        if (!this._recursiveSortingDepth)
+            return;
+        if (!--this._recursiveSortingDepth)
+            this.dispatchEventToListeners("sorting complete");
+    },
+
+    __proto__: WebInspector.DataGrid.prototype
+}
+
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotSortableDataGrid}
+ */
+WebInspector.HeapSnapshotViewportDataGrid = function(columns)
+{
+    WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
+    this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true);
+    this._topLevelNodes = [];
+    this._topPadding = new WebInspector.HeapSnapshotPaddingNode();
+    this._bottomPadding = new WebInspector.HeapSnapshotPaddingNode();
+    /**
+     * @type {WebInspector.HeapSnapshotGridNode}
+     */
+    this._nodeToHighlightAfterScroll = null;
+}
+
+WebInspector.HeapSnapshotViewportDataGrid.prototype = {
+    topLevelNodes: function()
+    {
+        return this._topLevelNodes;
+    },
+
+    appendChildAfterSorting: function(child)
+    {
+        // Do nothing here, it will be added in updateVisibleNodes.
+    },
+
+    updateVisibleNodes: function()
+    {
+        var scrollTop = this.scrollContainer.scrollTop;
+
+        var viewPortHeight = this.scrollContainer.offsetHeight;
+
+        this._removePaddingRows();
+
+        var children = this._topLevelNodes;
+
+        var i = 0;
+        var topPadding = 0;
+        while (i < children.length) {
+            if (children[i].revealed) {
+                var newTop = topPadding + children[i].nodeHeight();
+                if (newTop > scrollTop)
+                    break;
+                topPadding = newTop;
+            }
+            ++i;
+        }
+
+        this.rootNode().removeChildren();
+        // The height of the view port + invisible top part.
+        var heightToFill = viewPortHeight + (scrollTop - topPadding);
+        var filledHeight = 0;
+        while (i < children.length && filledHeight < heightToFill) {
+            if (children[i].revealed) {
+                this.rootNode().appendChild(children[i]);
+                filledHeight += children[i].nodeHeight();
+            }
+            ++i;
+        }
+
+        var bottomPadding = 0;
+        while (i < children.length) {
+            bottomPadding += children[i].nodeHeight();
+            ++i;
+        }
+
+        this._addPaddingRows(topPadding, bottomPadding);
+    },
+
+    appendTopLevelNode: function(node)
+    {
+        this._topLevelNodes.push(node);
+    },
+
+    removeTopLevelNodes: function()
+    {
+        this.rootNode().removeChildren();
+        this._topLevelNodes = [];
+    },
+
+    /**
+     * @override
+     * @param {WebInspector.HeapSnapshotGridNode} node
+     */
+    highlightNode: function(node)
+    {
+        if (this._isScrolledIntoView(node.element))
+            WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node);
+        else {
+            node.element.scrollIntoViewIfNeeded(true);
+            this._nodeToHighlightAfterScroll = node;
+        }
+    },
+
+    _isScrolledIntoView: function(element)
+    {
+        var viewportTop = this.scrollContainer.scrollTop;
+        var viewportBottom = viewportTop + this.scrollContainer.clientHeight;
+        var elemTop = element.offsetTop
+        var elemBottom = elemTop + element.offsetHeight;
+        return elemBottom <= viewportBottom && elemTop >= viewportTop;
+    },
+
+    _addPaddingRows: function(top, bottom)
+    {
+        if (this._topPadding.element.parentNode !== this.dataTableBody)
+            this.dataTableBody.insertBefore(this._topPadding.element, this.dataTableBody.firstChild);
+        if (this._bottomPadding.element.parentNode !== this.dataTableBody)
+            this.dataTableBody.insertBefore(this._bottomPadding.element, this.dataTableBody.lastChild);
+        this._topPadding.setHeight(top);
+        this._bottomPadding.setHeight(bottom);
+    },
+
+    _removePaddingRows: function()
+    {
+        this._bottomPadding.removeFromTable();
+        this._topPadding.removeFromTable();
+    },
+
+    onResize: function()
+    {
+        WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this);
+        this.updateVisibleNodes();
+    },
+
+    _onScroll: function(event)
+    {
+        this.updateVisibleNodes();
+
+        if (this._nodeToHighlightAfterScroll) {
+            WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll);
+            this._nodeToHighlightAfterScroll = null;
+        }
+    },
+
+    __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotPaddingNode = function()
+{
+    this.element = document.createElement("tr");
+    this.element.addStyleClass("revealed");
+}
+
+WebInspector.HeapSnapshotPaddingNode.prototype = {
+   setHeight: function(height)
+   {
+       this.element.style.height = height + "px";
+   },
+   removeFromTable: function()
+   {
+        var parent = this.element.parentNode;
+        if (parent)
+            parent.removeChild(this.element);
+   }
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotSortableDataGrid}
+ * @param {Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns
+ */
+WebInspector.HeapSnapshotContainmentDataGrid = function(columns)
+{
+    columns = columns || [
+        {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
+        {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
+        {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}
+    ];
+    WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
+}
+
+WebInspector.HeapSnapshotContainmentDataGrid.prototype = {
+    setDataSource: function(snapshot, nodeIndex)
+    {
+        this.snapshot = snapshot;
+        var node = new WebInspector.HeapSnapshotNode(snapshot, nodeIndex || snapshot.rootNodeIndex);
+        var fakeEdge = { node: node };
+        this.setRootNode(new WebInspector.HeapSnapshotObjectNode(this, false, fakeEdge, null));
+        this.rootNode().sort();
+    },
+
+    sortingChanged: function()
+    {
+        this.rootNode().sort();
+    },
+
+    __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotContainmentDataGrid}
+ */
+WebInspector.HeapSnapshotRetainmentDataGrid = function()
+{
+    this.showRetainingEdges = true;
+    var columns = [
+        {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
+        {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
+        {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true},
+        {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending}
+    ];
+    WebInspector.HeapSnapshotContainmentDataGrid.call(this, columns);
+}
+
+WebInspector.HeapSnapshotRetainmentDataGrid.Events = {
+    ExpandRetainersComplete: "ExpandRetainersComplete"
+}
+
+WebInspector.HeapSnapshotRetainmentDataGrid.prototype = {
+    _sortFields: function(sortColumn, sortAscending)
+    {
+        return {
+            object: ["_name", sortAscending, "_count", false],
+            count: ["_count", sortAscending, "_name", true],
+            shallowSize: ["_shallowSize", sortAscending, "_name", true],
+            retainedSize: ["_retainedSize", sortAscending, "_name", true],
+            distance: ["_distance", sortAscending, "_name", true]
+        }[sortColumn];
+    },
+
+    reset: function()
+    {
+        this.rootNode().removeChildren();
+        this.resetSortingCache();
+    },
+
+    /**
+     * @param {!WebInspector.HeapSnapshotProxy} snapshot
+     * @param {number} nodeIndex
+     */
+    setDataSource: function(snapshot, nodeIndex)
+    {
+        WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex);
+
+        var dataGrid = this;
+        var maxExpandLevels = 20;
+        /**
+         * @this {!WebInspector.HeapSnapshotObjectNode}
+         */
+        function populateComplete()
+        {
+            this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
+            this.expand();
+            if (--maxExpandLevels > 0 && this.children.length > 0 && (!this._distance || this._distance > 2)) {
+                var retainer = this.children[0];
+                retainer.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, retainer);
+                retainer.populate();
+            } else
+                dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete);
+        }
+        this.rootNode().addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this.rootNode());
+    },
+
+    __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotViewportDataGrid}
+ */
+WebInspector.HeapSnapshotConstructorsDataGrid = function()
+{
+    var columns = [
+        {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
+        {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true},
+        {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true},
+        {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
+        {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
+    ];
+    WebInspector.HeapSnapshotViewportDataGrid.call(this, columns);
+    this._profileIndex = -1;
+    this._topLevelNodes = [];
+
+    this._objectIdToSelect = null;
+}
+
+WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
+    _sortFields: function(sortColumn, sortAscending)
+    {
+        return {
+            object: ["_name", sortAscending, "_count", false],
+            distance: ["_distance", sortAscending, "_retainedSize", true],
+            count: ["_count", sortAscending, "_name", true],
+            shallowSize: ["_shallowSize", sortAscending, "_name", true],
+            retainedSize: ["_retainedSize", sortAscending, "_name", true]
+        }[sortColumn];
+    },
+
+    /**
+     * @override
+     * @param {HeapProfilerAgent.HeapSnapshotObjectId} id
+     */
+    highlightObjectByHeapSnapshotId: function(id)
+    {
+        if (!this.snapshot) {
+            this._objectIdToSelect = id;
+            return;
+        }
+
+        function didGetClassName(className)
+        {
+            var constructorNodes = this.topLevelNodes();
+            for (var i = 0; i < constructorNodes.length; i++) {
+                var parent = constructorNodes[i];
+                if (parent._name === className) {
+                    parent.revealNodeBySnapshotObjectId(parseInt(id, 10));
+                    return;
+                }
+            }
+        }
+        this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this));
+    },
+
+    setDataSource: function(snapshot)
+    {
+        this.snapshot = snapshot;
+        if (this._profileIndex === -1)
+            this._populateChildren();
+
+        if (this._objectIdToSelect) {
+            this.highlightObjectByHeapSnapshotId(this._objectIdToSelect);
+            this._objectIdToSelect = null;
+        }
+    },
+
+    _aggregatesReceived: function(key, aggregates)
+    {
+        for (var constructor in aggregates)
+            this.appendTopLevelNode(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], key));
+        this.sortingChanged();
+    },
+
+    _populateChildren: function()
+    {
+
+        this.dispose();
+        this.removeTopLevelNodes();
+        this.resetSortingCache();
+
+        var key = this._profileIndex === -1 ? "allObjects" : this._minNodeId + ".." + this._maxNodeId;
+        var filter = this._profileIndex === -1 ? null : "function(node) { var id = node.id(); return id > " + this._minNodeId + " && id <= " + this._maxNodeId + "; }";
+
+        this.snapshot.aggregates(false, key, filter, this._aggregatesReceived.bind(this, key));
+    },
+
+    filterSelectIndexChanged: function(profiles, profileIndex)
+    {
+        this._profileIndex = profileIndex;
+
+        delete this._maxNodeId;
+        delete this._minNodeId;
+
+        if (this._profileIndex !== -1) {
+            this._minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0;
+            this._maxNodeId = profiles[profileIndex].maxJSObjectId;
+        }
+
+        this._populateChildren();
+    },
+
+    __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotViewportDataGrid}
+ */
+WebInspector.HeapSnapshotDiffDataGrid = function()
+{
+    var columns = [
+        {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
+        {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true},
+        {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true},
+        {id: "countDelta", title: "# Delta", width: "64px", sortable: true},
+        {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
+        {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true},
+        {id: "sizeDelta", title: "Size Delta", width: "72px", sortable: true}
+    ];
+    WebInspector.HeapSnapshotViewportDataGrid.call(this, columns);
+}
+
+WebInspector.HeapSnapshotDiffDataGrid.prototype = {
+    /**
+     * @override
+     * @return {number}
+     */
+    defaultPopulateCount: function()
+    {
+        return 50;
+    },
+
+    _sortFields: function(sortColumn, sortAscending)
+    {
+        return {
+            object: ["_name", sortAscending, "_count", false],
+            addedCount: ["_addedCount", sortAscending, "_name", true],
+            removedCount: ["_removedCount", sortAscending, "_name", true],
+            countDelta: ["_countDelta", sortAscending, "_name", true],
+            addedSize: ["_addedSize", sortAscending, "_name", true],
+            removedSize: ["_removedSize", sortAscending, "_name", true],
+            sizeDelta: ["_sizeDelta", sortAscending, "_name", true]
+        }[sortColumn];
+    },
+
+    setDataSource: function(snapshot)
+    {
+        this.snapshot = snapshot;
+    },
+
+    /**
+     * @param {WebInspector.HeapSnapshotProxy} baseSnapshot
+     */
+    setBaseDataSource: function(baseSnapshot)
+    {
+        this.baseSnapshot = baseSnapshot;
+        this.dispose();
+        this.removeTopLevelNodes();
+        this.resetSortingCache();
+        if (this.baseSnapshot === this.snapshot) {
+            this.dispatchEventToListeners("sorting complete");
+            return;
+        }
+        this._populateChildren();
+    },
+
+    _populateChildren: function()
+    {
+        function aggregatesForDiffReceived(aggregatesForDiff)
+        {
+            this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this));
+            function didCalculateSnapshotDiff(diffByClassName)
+            {
+                for (var className in diffByClassName) {
+                    var diff = diffByClassName[className];
+                    this.appendTopLevelNode(new WebInspector.HeapSnapshotDiffNode(this, className, diff));
+                }
+                this.sortingChanged();
+            }
+        }
+        // Two snapshots live in different workers isolated from each other. That is why
+        // we first need to collect information about the nodes in the first snapshot and
+        // then pass it to the second snapshot to calclulate the diff.
+        this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this));
+    },
+
+    __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotSortableDataGrid}
+ */
+WebInspector.HeapSnapshotDominatorsDataGrid = function()
+{
+    var columns = [
+        {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
+        {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
+        {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
+    ];
+    WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
+    this._objectIdToSelect = null;
+}
+
+WebInspector.HeapSnapshotDominatorsDataGrid.prototype = {
+    /**
+     * @override
+     * @return {number}
+     */
+    defaultPopulateCount: function()
+    {
+        return 25;
+    },
+
+    setDataSource: function(snapshot)
+    {
+        this.snapshot = snapshot;
+
+        var fakeNode = { nodeIndex: this.snapshot.rootNodeIndex };
+        this.setRootNode(new WebInspector.HeapSnapshotDominatorObjectNode(this, fakeNode));
+        this.rootNode().sort();
+
+        if (this._objectIdToSelect) {
+            this.highlightObjectByHeapSnapshotId(this._objectIdToSelect);
+            this._objectIdToSelect = null;
+        }
+    },
+
+    sortingChanged: function()
+    {
+        this.rootNode().sort();
+    },
+
+    /**
+     * @override
+     * @param {HeapProfilerAgent.HeapSnapshotObjectId} id
+     */
+    highlightObjectByHeapSnapshotId: function(id)
+    {
+        if (!this.snapshot) {
+            this._objectIdToSelect = id;
+            return;
+        }
+
+        function didGetDominators(dominatorIds)
+        {
+            if (!dominatorIds) {
+                WebInspector.log(WebInspector.UIString("Cannot find corresponding heap snapshot node"));
+                return;
+            }
+            var dominatorNode = this.rootNode();
+            expandNextDominator.call(this, dominatorIds, dominatorNode);
+        }
+
+        function expandNextDominator(dominatorIds, dominatorNode)
+        {
+            if (!dominatorNode) {
+                console.error("Cannot find dominator node");
+                return;
+            }
+            if (!dominatorIds.length) {
+                this.highlightNode(dominatorNode);
+                dominatorNode.element.scrollIntoViewIfNeeded(true);
+                return;
+            }
+            var snapshotObjectId = dominatorIds.pop();
+            dominatorNode.retrieveChildBySnapshotObjectId(snapshotObjectId, expandNextDominator.bind(this, dominatorIds));
+        }
+
+        this.snapshot.dominatorIdsForNode(parseInt(id, 10), didGetDominators.bind(this));
+    },
+
+    __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
+}
+
diff --git a/Source/devtools/front_end/HeapSnapshotGridNodes.js b/Source/devtools/front_end/HeapSnapshotGridNodes.js
new file mode 100644
index 0000000..52fd825
--- /dev/null
+++ b/Source/devtools/front_end/HeapSnapshotGridNodes.js
@@ -0,0 +1,1119 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ * @param {WebInspector.HeapSnapshotSortableDataGrid} tree
+ * @param {boolean} hasChildren
+ */
+WebInspector.HeapSnapshotGridNode = function(tree, hasChildren)
+{
+    WebInspector.DataGridNode.call(this, null, hasChildren);
+    this._dataGrid = tree;
+    this._instanceCount = 0;
+
+    this._savedChildren = null;
+    /**
+     * List of position ranges for all visible nodes: [startPos1, endPos1),...,[startPosN, endPosN)
+     * Position is an item position in the provider.
+     */
+    this._retrievedChildrenRanges = [];
+}
+
+WebInspector.HeapSnapshotGridNode.Events = {
+    PopulateComplete: "PopulateComplete"
+}
+
+WebInspector.HeapSnapshotGridNode.prototype = {
+    /**
+     * @return {WebInspector.HeapSnapshotProviderProxy}
+     */
+    createProvider: function()
+    {
+        throw new Error("Needs implemented.");
+    },
+
+    /**
+     * @return {WebInspector.HeapSnapshotProviderProxy}
+     */
+    _provider: function()
+    {
+        if (!this._providerObject)
+            this._providerObject = this.createProvider();
+        return this._providerObject;
+    },
+
+    createCell: function(columnIdentifier)
+    {
+        var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
+        if (this._searchMatched)
+            cell.addStyleClass("highlight");
+        return cell;
+    },
+
+    collapse: function()
+    {
+        WebInspector.DataGridNode.prototype.collapse.call(this);
+        this._dataGrid.updateVisibleNodes();
+    },
+
+    dispose: function()
+    {
+        if (this._provider())
+            this._provider().dispose();
+        for (var node = this.children[0]; node; node = node.traverseNextNode(true, this, true))
+            if (node.dispose)
+                node.dispose();
+    },
+
+    _reachableFromWindow: false,
+
+    queryObjectContent: function(callback)
+    {
+    },
+
+    /**
+     * @override
+     */
+    wasDetached: function()
+    {
+        this._dataGrid.nodeWasDetached(this);
+    },
+
+    _toPercentString: function(num)
+    {
+        return num.toFixed(0) + "\u2009%"; // \u2009 is a thin space.
+    },
+
+    /**
+     * @param {number} nodePosition
+     */
+    childForPosition: function(nodePosition)
+    {
+        var indexOfFirsChildInRange = 0;
+        for (var i = 0; i < this._retrievedChildrenRanges.length; i++) {
+           var range = this._retrievedChildrenRanges[i];
+           if (range.from <= nodePosition && nodePosition < range.to) {
+               var childIndex = indexOfFirsChildInRange + nodePosition - range.from;
+               return this.children[childIndex];
+           }
+           indexOfFirsChildInRange += range.to - range.from + 1;
+        }
+        return null;
+    },
+
+    _createValueCell: function(columnIdentifier)
+    {
+        var cell = document.createElement("td");
+        cell.className = columnIdentifier + "-column";
+        if (this.dataGrid.snapshot.totalSize !== 0) {
+            var div = document.createElement("div");
+            var valueSpan = document.createElement("span");
+            valueSpan.textContent = this.data[columnIdentifier];
+            div.appendChild(valueSpan);
+            var percentColumn = columnIdentifier + "-percent";
+            if (percentColumn in this.data) {
+                var percentSpan = document.createElement("span");
+                percentSpan.className = "percent-column";
+                percentSpan.textContent = this.data[percentColumn];
+                div.appendChild(percentSpan);
+                div.addStyleClass("heap-snapshot-multiple-values");
+            }
+            cell.appendChild(div);
+        }
+        return cell;
+    },
+
+    populate: function(event)
+    {
+        if (this._populated)
+            return;
+        this._populated = true;
+
+        function sorted()
+        {
+            this._populateChildren();
+        }
+        this._provider().sortAndRewind(this.comparator(), sorted.bind(this));
+    },
+
+    expandWithoutPopulate: function(callback)
+    {
+        // Make sure default populate won't take action.
+        this._populated = true;
+        this.expand();
+        this._provider().sortAndRewind(this.comparator(), callback);
+    },
+
+    /**
+     * @param {?number} fromPosition
+     * @param {?number} toPosition
+     */
+    _populateChildren: function(fromPosition, toPosition, afterPopulate)
+    {
+        fromPosition = fromPosition || 0;
+        toPosition = toPosition || fromPosition + this._dataGrid.defaultPopulateCount();
+        var firstNotSerializedPosition = fromPosition;
+        function serializeNextChunk()
+        {
+            if (firstNotSerializedPosition >= toPosition)
+                return;
+            var end = Math.min(firstNotSerializedPosition + this._dataGrid.defaultPopulateCount(), toPosition);
+            this._provider().serializeItemsRange(firstNotSerializedPosition, end, childrenRetrieved.bind(this));
+            firstNotSerializedPosition = end;
+        }
+        function insertRetrievedChild(item, insertionIndex)
+        {
+            if (this._savedChildren) {
+                var hash = this._childHashForEntity(item);
+                if (hash in this._savedChildren) {
+                    this.insertChild(this._savedChildren[hash], insertionIndex);
+                    return;
+                }
+            }
+            this.insertChild(this._createChildNode(item), insertionIndex);
+        }
+        function insertShowMoreButton(from, to, insertionIndex)
+        {
+            var button = new WebInspector.ShowMoreDataGridNode(this._populateChildren.bind(this), from, to, this._dataGrid.defaultPopulateCount());
+            this.insertChild(button, insertionIndex);
+        }
+        function childrenRetrieved(items)
+        {
+            var itemIndex = 0;
+            var itemPosition = items.startPosition;
+            var insertionIndex = 0;
+
+            if (!this._retrievedChildrenRanges.length) {
+                if (items.startPosition > 0) {
+                    this._retrievedChildrenRanges.push({from: 0, to: 0});
+                    insertShowMoreButton.call(this, 0, items.startPosition, insertionIndex++);
+                }
+                this._retrievedChildrenRanges.push({from: items.startPosition, to: items.endPosition});
+                for (var i = 0, l = items.length; i < l; ++i)
+                    insertRetrievedChild.call(this, items[i], insertionIndex++);
+                if (items.endPosition < items.totalLength)
+                    insertShowMoreButton.call(this, items.endPosition, items.totalLength, insertionIndex++);
+            } else {
+                var rangeIndex = 0;
+                var found = false;
+                var range;
+                while (rangeIndex < this._retrievedChildrenRanges.length) {
+                    range = this._retrievedChildrenRanges[rangeIndex];
+                    if (range.to >= itemPosition) {
+                        found = true;
+                        break;
+                    }
+                    insertionIndex += range.to - range.from;
+                    // Skip the button if there is one.
+                    if (range.to < items.totalLength)
+                        insertionIndex += 1;
+                    ++rangeIndex;
+                }
+
+                if (!found || items.startPosition < range.from) {
+                    // Update previous button.
+                    this.children[insertionIndex - 1].setEndPosition(items.startPosition);
+                    insertShowMoreButton.call(this, items.startPosition, found ? range.from : items.totalLength, insertionIndex);
+                    range = {from: items.startPosition, to: items.startPosition};
+                    if (!found)
+                        rangeIndex = this._retrievedChildrenRanges.length;
+                    this._retrievedChildrenRanges.splice(rangeIndex, 0, range);
+                } else {
+                    insertionIndex += itemPosition - range.from;
+                }
+                // At this point insertionIndex is always an index before button or between nodes.
+                // Also it is always true here that range.from <= itemPosition <= range.to
+
+                // Stretch the range right bound to include all new items.
+                while (range.to < items.endPosition) {
+                    // Skip already added nodes.
+                    var skipCount = range.to - itemPosition;
+                    insertionIndex += skipCount;
+                    itemIndex += skipCount;
+                    itemPosition = range.to;
+
+                    // We're at the position before button: ...<?node>x<button>
+                    var nextRange = this._retrievedChildrenRanges[rangeIndex + 1];
+                    var newEndOfRange = nextRange ? nextRange.from : items.totalLength;
+                    if (newEndOfRange > items.endPosition)
+                        newEndOfRange = items.endPosition;
+                    while (itemPosition < newEndOfRange) {
+                        insertRetrievedChild.call(this, items[itemIndex++], insertionIndex++);
+                        ++itemPosition;
+                    }
+                    // Merge with the next range.
+                    if (nextRange && newEndOfRange === nextRange.from) {
+                        range.to = nextRange.to;
+                        // Remove "show next" button if there is one.
+                        this.removeChild(this.children[insertionIndex]);
+                        this._retrievedChildrenRanges.splice(rangeIndex + 1, 1);
+                    } else {
+                        range.to = newEndOfRange;
+                        // Remove or update next button.
+                        if (newEndOfRange === items.totalLength)
+                            this.removeChild(this.children[insertionIndex]);
+                        else
+                            this.children[insertionIndex].setStartPosition(items.endPosition);
+                    }
+                }
+            }
+
+            // TODO: fix this.
+            this._instanceCount += items.length;
+            if (firstNotSerializedPosition < toPosition) {
+                serializeNextChunk.call(this);
+                return;
+            }
+
+            if (afterPopulate)
+                afterPopulate();
+            this.dispatchEventToListeners(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete);
+        }
+        serializeNextChunk.call(this);
+    },
+
+    _saveChildren: function()
+    {
+        this._savedChildren = null;
+        for (var i = 0, childrenCount = this.children.length; i < childrenCount; ++i) {
+            var child = this.children[i];
+            if (!child.expanded)
+                continue;
+            if (!this._savedChildren)
+                this._savedChildren = {};
+            this._savedChildren[this._childHashForNode(child)] = child;
+        }
+    },
+
+    sort: function()
+    {
+        this._dataGrid.recursiveSortingEnter();
+        function afterSort()
+        {
+            this._saveChildren();
+            this.removeChildren();
+            this._retrievedChildrenRanges = [];
+
+            function afterPopulate()
+            {
+                for (var i = 0, l = this.children.length; i < l; ++i) {
+                    var child = this.children[i];
+                    if (child.expanded)
+                        child.sort();
+                }
+                this._dataGrid.recursiveSortingLeave();
+            }
+            var instanceCount = this._instanceCount;
+            this._instanceCount = 0;
+            this._populateChildren(0, instanceCount, afterPopulate.bind(this));
+        }
+
+        this._provider().sortAndRewind(this.comparator(), afterSort.bind(this));
+    },
+
+    __proto__: WebInspector.DataGridNode.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGridNode}
+ * @param {WebInspector.HeapSnapshotSortableDataGrid} tree
+ */
+WebInspector.HeapSnapshotGenericObjectNode = function(tree, node)
+{
+    this.snapshotNodeIndex = 0;
+    WebInspector.HeapSnapshotGridNode.call(this, tree, false);
+    // node is null for DataGrid root nodes.
+    if (!node)
+        return;
+    this._name = node.name;
+    this._displayName = node.displayName;
+    this._type = node.type;
+    this._distance = node.distance;
+    this._shallowSize = node.selfSize;
+    this._retainedSize = node.retainedSize;
+    this.snapshotNodeId = node.id;
+    this.snapshotNodeIndex = node.nodeIndex;
+    if (this._type === "string")
+        this._reachableFromWindow = true;
+    else if (this._type === "object" && this._name.startsWith("Window")) {
+        this._name = this.shortenWindowURL(this._name, false);
+        this._reachableFromWindow = true;
+    } else if (node.canBeQueried)
+        this._reachableFromWindow = true;
+    if (node.detachedDOMTreeNode)
+        this.detachedDOMTreeNode = true;
+};
+
+WebInspector.HeapSnapshotGenericObjectNode.prototype = {
+    createCell: function(columnIdentifier)
+    {
+        var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : this._createObjectCell();
+        if (this._searchMatched)
+            cell.addStyleClass("highlight");
+        return cell;
+    },
+
+    _createObjectCell: function()
+    {
+        var cell = document.createElement("td");
+        cell.className = "object-column";
+        var div = document.createElement("div");
+        div.className = "source-code event-properties";
+        div.style.overflow = "visible";
+
+        var data = this.data["object"];
+        if (this._prefixObjectCell)
+            this._prefixObjectCell(div, data);
+
+        var valueSpan = document.createElement("span");
+        valueSpan.className = "value console-formatted-" + data.valueStyle;
+        valueSpan.textContent = data.value;
+        div.appendChild(valueSpan);
+
+        if (this.data.displayName) {
+            var nameSpan = document.createElement("span");
+            nameSpan.className = "name console-formatted-name";
+            nameSpan.textContent = " " + this.data.displayName;
+            div.appendChild(nameSpan);
+        }
+
+        var idSpan = document.createElement("span");
+        idSpan.className = "console-formatted-id";
+        idSpan.textContent = " @" + data["nodeId"];
+        div.appendChild(idSpan);
+
+        if (this._postfixObjectCell)
+            this._postfixObjectCell(div, data);
+
+        cell.appendChild(div);
+        cell.addStyleClass("disclosure");
+        if (this.depth)
+            cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
+        cell.heapSnapshotNode = this;
+        return cell;
+    },
+
+    get data()
+    {
+        var data = this._emptyData();
+
+        var value = this._name;
+        var valueStyle = "object";
+        switch (this._type) {
+        case "string":
+            value = "\"" + value + "\"";
+            valueStyle = "string";
+            break;
+        case "regexp":
+            value = "/" + value + "/";
+            valueStyle = "string";
+            break;
+        case "closure":
+            value = "function" + (value ? " " : "") + value + "()";
+            valueStyle = "function";
+            break;
+        case "number":
+            valueStyle = "number";
+            break;
+        case "hidden":
+            valueStyle = "null";
+            break;
+        case "array":
+            if (!value)
+                value = "[]";
+            else
+                value += "[]";
+            break;
+        };
+        if (this._reachableFromWindow)
+            valueStyle += " highlight";
+        if (value === "Object")
+            value = "";
+        if (this.detachedDOMTreeNode)
+            valueStyle += " detached-dom-tree-node";
+        data["object"] = { valueStyle: valueStyle, value: value, nodeId: this.snapshotNodeId };
+
+        data["displayName"] = this._displayName;
+        data["distance"] =  this._distance;
+        data["shallowSize"] = Number.withThousandsSeparator(this._shallowSize);
+        data["retainedSize"] = Number.withThousandsSeparator(this._retainedSize);
+        data["shallowSize-percent"] = this._toPercentString(this._shallowSizePercent);
+        data["retainedSize-percent"] = this._toPercentString(this._retainedSizePercent);
+
+        return this._enhanceData ? this._enhanceData(data) : data;
+    },
+
+    queryObjectContent: function(callback, objectGroupName)
+    {
+        if (this._type === "string")
+            callback(WebInspector.RemoteObject.fromPrimitiveValue(this._name));
+        else {
+            function formatResult(error, object)
+            {
+                if (!error && object.type)
+                    callback(WebInspector.RemoteObject.fromPayload(object), !!error);
+                else
+                    callback(WebInspector.RemoteObject.fromPrimitiveValue(WebInspector.UIString("Not available")));
+            }
+            HeapProfilerAgent.getObjectByHeapObjectId(String(this.snapshotNodeId), objectGroupName, formatResult);
+        }
+    },
+
+    get _retainedSizePercent()
+    {
+        return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
+    },
+
+    get _shallowSizePercent()
+    {
+        return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
+    },
+
+    updateHasChildren: function()
+    {
+        function isEmptyCallback(isEmpty)
+        {
+            this.hasChildren = !isEmpty;
+        }
+        this._provider().isEmpty(isEmptyCallback.bind(this));
+    },
+
+    shortenWindowURL: function(fullName, hasObjectId)
+    {
+        var startPos = fullName.indexOf("/");
+        var endPos = hasObjectId ? fullName.indexOf("@") : fullName.length;
+        if (startPos !== -1 && endPos !== -1) {
+            var fullURL = fullName.substring(startPos + 1, endPos).trimLeft();
+            var url = fullURL.trimURL();
+            if (url.length > 40)
+                url = url.trimMiddle(40);
+            return fullName.substr(0, startPos + 2) + url + fullName.substr(endPos);
+        } else
+            return fullName;
+    },
+
+    __proto__: WebInspector.HeapSnapshotGridNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGenericObjectNode}
+ * @param {WebInspector.HeapSnapshotSortableDataGrid} tree
+ * @param {boolean} isFromBaseSnapshot
+ */
+WebInspector.HeapSnapshotObjectNode = function(tree, isFromBaseSnapshot, edge, parentGridNode)
+{
+    WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, edge.node);
+    this._referenceName = edge.name;
+    this._referenceType = edge.type;
+    this._distance = edge.distance;
+    this.showRetainingEdges = tree.showRetainingEdges;
+    this._isFromBaseSnapshot = isFromBaseSnapshot;
+
+    this._parentGridNode = parentGridNode;
+    this._cycledWithAncestorGridNode = this._findAncestorWithSameSnapshotNodeId();
+    if (!this._cycledWithAncestorGridNode)
+        this.updateHasChildren();
+}
+
+WebInspector.HeapSnapshotObjectNode.prototype = {
+    /**
+     * @return {WebInspector.HeapSnapshotProviderProxy}
+     */
+    createProvider: function()
+    {
+        var tree = this._dataGrid;
+        var showHiddenData = WebInspector.settings.showHeapSnapshotObjectsHiddenProperties.get();
+        var snapshot = this._isFromBaseSnapshot ? tree.baseSnapshot : tree.snapshot;
+        if (this.showRetainingEdges)
+            return snapshot.createRetainingEdgesProvider(this.snapshotNodeIndex, showHiddenData);
+        else
+            return snapshot.createEdgesProvider(this.snapshotNodeIndex, showHiddenData);
+    },
+
+    _findAncestorWithSameSnapshotNodeId: function()
+    {
+        var ancestor = this._parentGridNode;
+        while (ancestor) {
+            if (ancestor.snapshotNodeId === this.snapshotNodeId)
+                return ancestor;
+            ancestor = ancestor._parentGridNode;
+        }
+        return null;
+    },
+
+    _createChildNode: function(item)
+    {
+        return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._isFromBaseSnapshot, item, this);
+    },
+
+    _childHashForEntity: function(edge)
+    {
+        var prefix = this.showRetainingEdges ? edge.node.id + "#" : "";
+        return prefix + edge.type + "#" + edge.name;
+    },
+
+    _childHashForNode: function(childNode)
+    {
+        var prefix = this.showRetainingEdges ? childNode.snapshotNodeId + "#" : "";
+        return prefix + childNode._referenceType + "#" + childNode._referenceName;
+    },
+
+    comparator: function()
+    {
+        var sortAscending = this._dataGrid.isSortOrderAscending();
+        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+        var sortFields = {
+            object: ["!edgeName", sortAscending, "retainedSize", false],
+            count: ["!edgeName", true, "retainedSize", false],
+            shallowSize: ["selfSize", sortAscending, "!edgeName", true],
+            retainedSize: ["retainedSize", sortAscending, "!edgeName", true],
+            distance: ["distance", sortAscending, "_name", true]
+        }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
+        return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
+    },
+
+    _emptyData: function()
+    {
+        return { count: "", addedCount: "", removedCount: "", countDelta: "", addedSize: "", removedSize: "", sizeDelta: "" };
+    },
+
+    _enhanceData: function(data)
+    {
+        var name = this._referenceName;
+        if (name === "") name = "(empty)";
+        var nameClass = "name";
+        switch (this._referenceType) {
+        case "context":
+            nameClass = "console-formatted-number";
+            break;
+        case "internal":
+        case "hidden":
+            nameClass = "console-formatted-null";
+            break;
+        case "element":
+            name = "[" + name + "]";
+            break;
+        }
+        data["object"].nameClass = nameClass;
+        data["object"].name = name;
+        data["distance"] = this._distance;
+        return data;
+    },
+
+    _prefixObjectCell: function(div, data)
+    {
+        if (this._cycledWithAncestorGridNode)
+            div.className += " cycled-ancessor-node";
+
+        var nameSpan = document.createElement("span");
+        nameSpan.className = data.nameClass;
+        nameSpan.textContent = data.name;
+        div.appendChild(nameSpan);
+
+        var separatorSpan = document.createElement("span");
+        separatorSpan.className = "grayed";
+        separatorSpan.textContent = this.showRetainingEdges ? " in " : " :: ";
+        div.appendChild(separatorSpan);
+    },
+
+    __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGenericObjectNode}
+ */
+WebInspector.HeapSnapshotInstanceNode = function(tree, baseSnapshot, snapshot, node)
+{
+    WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
+    this._baseSnapshotOrSnapshot = baseSnapshot || snapshot;
+    this._isDeletedNode = !!baseSnapshot;
+    this.updateHasChildren();
+};
+
+WebInspector.HeapSnapshotInstanceNode.prototype = {
+    createProvider: function()
+    {
+        var showHiddenData = WebInspector.settings.showHeapSnapshotObjectsHiddenProperties.get();
+        return this._baseSnapshotOrSnapshot.createEdgesProvider(
+            this.snapshotNodeIndex,
+            showHiddenData);
+    },
+
+    _createChildNode: function(item)
+    {
+        return new WebInspector.HeapSnapshotObjectNode(this._dataGrid, this._isDeletedNode, item, null);
+    },
+
+    _childHashForEntity: function(edge)
+    {
+        return edge.type + "#" + edge.name;
+    },
+
+    _childHashForNode: function(childNode)
+    {
+        return childNode._referenceType + "#" + childNode._referenceName;
+    },
+
+    comparator: function()
+    {
+        var sortAscending = this._dataGrid.isSortOrderAscending();
+        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+        var sortFields = {
+            object: ["!edgeName", sortAscending, "retainedSize", false],
+            distance: ["distance", sortAscending, "retainedSize", false],
+            count: ["!edgeName", true, "retainedSize", false],
+            addedSize: ["selfSize", sortAscending, "!edgeName", true],
+            removedSize: ["selfSize", sortAscending, "!edgeName", true],
+            shallowSize: ["selfSize", sortAscending, "!edgeName", true],
+            retainedSize: ["retainedSize", sortAscending, "!edgeName", true]
+        }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false];
+        return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
+    },
+
+    _emptyData: function()
+    {
+        return {count: "", countDelta: "", sizeDelta: ""};
+    },
+
+    _enhanceData: function(data)
+    {
+        if (this._isDeletedNode) {
+            data["addedCount"] = "";
+            data["addedSize"] = "";
+            data["removedCount"] = "\u2022";
+            data["removedSize"] = Number.withThousandsSeparator(this._shallowSize);
+        } else {
+            data["addedCount"] = "\u2022";
+            data["addedSize"] = Number.withThousandsSeparator(this._shallowSize);
+            data["removedCount"] = "";
+            data["removedSize"] = "";
+        }
+        return data;
+    },
+
+    get isDeletedNode()
+    {
+        return this._isDeletedNode;
+    },
+
+    __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGridNode}
+ */
+WebInspector.HeapSnapshotConstructorNode = function(tree, className, aggregate, aggregatesKey)
+{
+    WebInspector.HeapSnapshotGridNode.call(this, tree, aggregate.count > 0);
+    this._name = className;
+    this._aggregatesKey = aggregatesKey;
+    this._distance = aggregate.distance;
+    this._count = aggregate.count;
+    this._shallowSize = aggregate.self;
+    this._retainedSize = aggregate.maxRet;
+}
+
+WebInspector.HeapSnapshotConstructorNode.prototype = {
+    /**
+     * @override
+     * @return {WebInspector.HeapSnapshotProviderProxy}
+     */
+    createProvider: function()
+    {
+        return this._dataGrid.snapshot.createNodesProviderForClass(this._name, this._aggregatesKey)
+    },
+
+    /**
+     * @param {number} snapshotObjectId
+     */
+    revealNodeBySnapshotObjectId: function(snapshotObjectId)
+    {
+        function didExpand()
+        {
+            this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this));
+        }
+
+        function didGetNodePosition(nodePosition)
+        {
+            if (nodePosition === -1)
+                this.collapse();
+            else
+                this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition));
+        }
+
+        function didPopulateChildren(nodePosition)
+        {
+            var indexOfFirsChildInRange = 0;
+            for (var i = 0; i < this._retrievedChildrenRanges.length; i++) {
+               var range = this._retrievedChildrenRanges[i];
+               if (range.from <= nodePosition && nodePosition < range.to) {
+                   var childIndex = indexOfFirsChildInRange + nodePosition - range.from;
+                   var instanceNode = this.children[childIndex];
+                   this._dataGrid.highlightNode(instanceNode);
+                   return;
+               }
+               indexOfFirsChildInRange += range.to - range.from + 1;
+            }
+        }
+
+        this.expandWithoutPopulate(didExpand.bind(this));
+    },
+
+    createCell: function(columnIdentifier)
+    {
+        var cell = columnIdentifier !== "object" ? this._createValueCell(columnIdentifier) : WebInspector.HeapSnapshotGridNode.prototype.createCell.call(this, columnIdentifier);
+        if (this._searchMatched)
+            cell.addStyleClass("highlight");
+        return cell;
+    },
+
+    _createChildNode: function(item)
+    {
+        return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, null, this._dataGrid.snapshot, item);
+    },
+
+    comparator: function()
+    {
+        var sortAscending = this._dataGrid.isSortOrderAscending();
+        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+        var sortFields = {
+            object: ["id", sortAscending, "retainedSize", false],
+            distance: ["distance", true, "retainedSize", false],
+            count: ["id", true, "retainedSize", false],
+            shallowSize: ["selfSize", sortAscending, "id", true],
+            retainedSize: ["retainedSize", sortAscending, "id", true]
+        }[sortColumnIdentifier];
+        return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
+    },
+
+    _childHashForEntity: function(node)
+    {
+        return node.id;
+    },
+
+    _childHashForNode: function(childNode)
+    {
+        return childNode.snapshotNodeId;
+    },
+
+    get data()
+    {
+        var data = { object: this._name };
+        data["count"] =  Number.withThousandsSeparator(this._count);
+        data["distance"] =  this._distance;
+        data["shallowSize"] = Number.withThousandsSeparator(this._shallowSize);
+        data["retainedSize"] = Number.withThousandsSeparator(this._retainedSize);
+        data["count-percent"] =  this._toPercentString(this._countPercent);
+        data["shallowSize-percent"] = this._toPercentString(this._shallowSizePercent);
+        data["retainedSize-percent"] = this._toPercentString(this._retainedSizePercent);
+        return data;
+    },
+
+    get _countPercent()
+    {
+        return this._count / this.dataGrid.snapshot.nodeCount * 100.0;
+    },
+
+    get _retainedSizePercent()
+    {
+        return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0;
+    },
+
+    get _shallowSizePercent()
+    {
+        return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0;
+    },
+
+    __proto__: WebInspector.HeapSnapshotGridNode.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotProviderProxy}
+ * @param {WebInspector.HeapSnapshotProviderProxy} addedNodesProvider
+ * @param {WebInspector.HeapSnapshotProviderProxy} deletedNodesProvider
+ */
+WebInspector.HeapSnapshotDiffNodesProvider = function(addedNodesProvider, deletedNodesProvider, addedCount, removedCount)
+{
+    this._addedNodesProvider = addedNodesProvider;
+    this._deletedNodesProvider = deletedNodesProvider;
+    this._addedCount = addedCount;
+    this._removedCount = removedCount;
+}
+
+WebInspector.HeapSnapshotDiffNodesProvider.prototype = {
+    dispose: function()
+    {
+        this._addedNodesProvider.dispose();
+        this._deletedNodesProvider.dispose();
+    },
+
+    isEmpty: function(callback)
+    {
+        callback(false);
+    },
+
+    serializeItemsRange: function(beginPosition, endPosition, callback)
+    {
+        function didReceiveAllItems(items)
+        {
+            items.totalLength = this._addedCount + this._removedCount;
+            callback(items);
+        }
+
+        function didReceiveDeletedItems(addedItems, items)
+        {
+            if (!addedItems.length)
+                addedItems.startPosition = this._addedCount + items.startPosition;
+            for (var i = 0; i < items.length; i++) {
+                items[i].isAddedNotRemoved = false;
+                addedItems.push(items[i]);
+            }
+            addedItems.endPosition = this._addedCount + items.endPosition;
+            didReceiveAllItems.call(this, addedItems);
+        }
+
+        function didReceiveAddedItems(items)
+        {
+            for (var i = 0; i < items.length; i++)
+                items[i].isAddedNotRemoved = true;
+            if (items.endPosition < endPosition)
+                return this._deletedNodesProvider.serializeItemsRange(0, endPosition - items.endPosition, didReceiveDeletedItems.bind(this, items));
+
+            items.totalLength = this._addedCount + this._removedCount;
+            didReceiveAllItems.call(this, items);
+        }
+
+        if (beginPosition < this._addedCount)
+            this._addedNodesProvider.serializeItemsRange(beginPosition, endPosition, didReceiveAddedItems.bind(this));
+        else
+            this._deletedNodesProvider.serializeItemsRange(beginPosition - this._addedCount, endPosition - this._addedCount, didReceiveDeletedItems.bind(this, []));
+    },
+
+    sortAndRewind: function(comparator, callback)
+    {
+        function afterSort()
+        {
+            this._deletedNodesProvider.sortAndRewind(comparator, callback);
+        }
+        this._addedNodesProvider.sortAndRewind(comparator, afterSort.bind(this));
+    }
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGridNode}
+ */
+WebInspector.HeapSnapshotDiffNode = function(tree, className, diffForClass)
+{
+    WebInspector.HeapSnapshotGridNode.call(this, tree, true);
+    this._name = className;
+
+    this._addedCount = diffForClass.addedCount;
+    this._removedCount = diffForClass.removedCount;
+    this._countDelta = diffForClass.countDelta;
+    this._addedSize = diffForClass.addedSize;
+    this._removedSize = diffForClass.removedSize;
+    this._sizeDelta = diffForClass.sizeDelta;
+    this._deletedIndexes = diffForClass.deletedIndexes;
+}
+
+WebInspector.HeapSnapshotDiffNode.prototype = {
+    /**
+     * @override
+     * @return {WebInspector.HeapSnapshotDiffNodesProvider}
+     */
+    createProvider: function()
+    {
+        var tree = this._dataGrid;
+        return  new WebInspector.HeapSnapshotDiffNodesProvider(
+            tree.snapshot.createAddedNodesProvider(tree.baseSnapshot.uid, this._name),
+            tree.baseSnapshot.createDeletedNodesProvider(this._deletedIndexes),
+            this._addedCount,
+            this._removedCount);
+    },
+
+    _createChildNode: function(item)
+    {
+        if (item.isAddedNotRemoved)
+            return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, null, this._dataGrid.snapshot, item);
+        else
+            return new WebInspector.HeapSnapshotInstanceNode(this._dataGrid, this._dataGrid.baseSnapshot, null, item);
+    },
+
+    _childHashForEntity: function(node)
+    {
+        return node.id;
+    },
+
+    _childHashForNode: function(childNode)
+    {
+        return childNode.snapshotNodeId;
+    },
+
+    comparator: function()
+    {
+        var sortAscending = this._dataGrid.isSortOrderAscending();
+        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+        var sortFields = {
+            object: ["id", sortAscending, "selfSize", false],
+            addedCount: ["selfSize", sortAscending, "id", true],
+            removedCount: ["selfSize", sortAscending, "id", true],
+            countDelta: ["selfSize", sortAscending, "id", true],
+            addedSize: ["selfSize", sortAscending, "id", true],
+            removedSize: ["selfSize", sortAscending, "id", true],
+            sizeDelta: ["selfSize", sortAscending, "id", true]
+        }[sortColumnIdentifier];
+        return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
+    },
+
+    _signForDelta: function(delta)
+    {
+        if (delta === 0)
+            return "";
+        if (delta > 0)
+            return "+";
+        else
+            return "\u2212";  // Math minus sign, same width as plus.
+    },
+
+    get data()
+    {
+        var data = {object: this._name};
+
+        data["addedCount"] = Number.withThousandsSeparator(this._addedCount);
+        data["removedCount"] = Number.withThousandsSeparator(this._removedCount);
+        data["countDelta"] = this._signForDelta(this._countDelta) + Number.withThousandsSeparator(Math.abs(this._countDelta));
+        data["addedSize"] = Number.withThousandsSeparator(this._addedSize);
+        data["removedSize"] = Number.withThousandsSeparator(this._removedSize);
+        data["sizeDelta"] = this._signForDelta(this._sizeDelta) + Number.withThousandsSeparator(Math.abs(this._sizeDelta));
+
+        return data;
+    },
+
+    __proto__: WebInspector.HeapSnapshotGridNode.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotGenericObjectNode}
+ */
+WebInspector.HeapSnapshotDominatorObjectNode = function(tree, node)
+{
+    WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node);
+    this.updateHasChildren();
+};
+
+WebInspector.HeapSnapshotDominatorObjectNode.prototype = {
+    /**
+     * @override
+     * @return {WebInspector.HeapSnapshotProviderProxy}
+     */
+    createProvider: function()
+    {
+        return this._dataGrid.snapshot.createNodesProviderForDominator(this.snapshotNodeIndex);
+    },
+
+    /**
+     * @param {number} snapshotObjectId
+     * @param {function(?WebInspector.HeapSnapshotDominatorObjectNode)} callback
+     */
+    retrieveChildBySnapshotObjectId: function(snapshotObjectId, callback)
+    {
+        function didExpand()
+        {
+            this._provider().nodePosition(snapshotObjectId, didGetNodePosition.bind(this));
+        }
+
+        function didGetNodePosition(nodePosition)
+        {
+            if (nodePosition === -1) {
+                this.collapse();
+                callback(null);
+            } else
+                this._populateChildren(nodePosition, null, didPopulateChildren.bind(this, nodePosition));
+        }
+
+        function didPopulateChildren(nodePosition)
+        {
+            var child = this.childForPosition(nodePosition);
+            callback(child);
+        }
+
+        // Make sure hasChildren flag is updated before expanding this node as updateHasChildren response
+        // may not have been received yet.
+        this.hasChildren = true;
+        this.expandWithoutPopulate(didExpand.bind(this));
+    },
+
+    _createChildNode: function(item)
+    {
+        return new WebInspector.HeapSnapshotDominatorObjectNode(this._dataGrid, item);
+    },
+
+    _childHashForEntity: function(node)
+    {
+        return node.id;
+    },
+
+    _childHashForNode: function(childNode)
+    {
+        return childNode.snapshotNodeId;
+    },
+
+    comparator: function()
+    {
+        var sortAscending = this._dataGrid.isSortOrderAscending();
+        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier();
+        var sortFields = {
+            object: ["id", sortAscending, "retainedSize", false],
+            shallowSize: ["selfSize", sortAscending, "id", true],
+            retainedSize: ["retainedSize", sortAscending, "id", true]
+        }[sortColumnIdentifier];
+        return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields);
+    },
+
+    _emptyData: function()
+    {
+        return {};
+    },
+
+    __proto__: WebInspector.HeapSnapshotGenericObjectNode.prototype
+}
+
diff --git a/Source/devtools/front_end/HeapSnapshotLoader.js b/Source/devtools/front_end/HeapSnapshotLoader.js
new file mode 100644
index 0000000..12cf8fe
--- /dev/null
+++ b/Source/devtools/front_end/HeapSnapshotLoader.js
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.OutputStream}
+ */
+WebInspector.HeapSnapshotLoader = function()
+{
+    this._reset();
+}
+
+WebInspector.HeapSnapshotLoader.prototype = {
+    dispose: function()
+    {
+        this._reset();
+    },
+
+    _reset: function()
+    {
+        this._json = "";
+        this._state = "find-snapshot-info";
+        this._snapshot = {};
+    },
+
+    close: function()
+    {
+        if (this._json)
+            this._parseStringsArray();
+    },
+
+    buildSnapshot: function(constructorName)
+    {
+        var constructor = WebInspector[constructorName];
+        var result = new constructor(this._snapshot);
+        this._reset();
+        return result;
+    },
+
+    _parseUintArray: function()
+    {
+        var index = 0;
+        var char0 = "0".charCodeAt(0), char9 = "9".charCodeAt(0), closingBracket = "]".charCodeAt(0);
+        var length = this._json.length;
+        while (true) {
+            while (index < length) {
+                var code = this._json.charCodeAt(index);
+                if (char0 <= code && code <= char9)
+                    break;
+                else if (code === closingBracket) {
+                    this._json = this._json.slice(index + 1);
+                    return false;
+                }
+                ++index;
+            }
+            if (index === length) {
+                this._json = "";
+                return true;
+            }
+            var nextNumber = 0;
+            var startIndex = index;
+            while (index < length) {
+                var code = this._json.charCodeAt(index);
+                if (char0 > code || code > char9)
+                    break;
+                nextNumber *= 10;
+                nextNumber += (code - char0);
+                ++index;
+            }
+            if (index === length) {
+                this._json = this._json.slice(startIndex);
+                return true;
+            }
+            this._array[this._arrayIndex++] = nextNumber;
+        }
+    },
+
+    _parseStringsArray: function()
+    {
+        var closingBracketIndex = this._json.lastIndexOf("]");
+        if (closingBracketIndex === -1)
+            throw new Error("Incomplete JSON");
+        this._json = this._json.slice(0, closingBracketIndex + 1);
+        this._snapshot.strings = JSON.parse(this._json);
+    },
+
+    /**
+     * @param {string} chunk
+     */
+    write: function(chunk)
+    {
+        this._json += chunk;
+        switch (this._state) {
+        case "find-snapshot-info": {
+            var snapshotToken = "\"snapshot\"";
+            var snapshotTokenIndex = this._json.indexOf(snapshotToken);
+            if (snapshotTokenIndex === -1)
+                throw new Error("Snapshot token not found");
+            this._json = this._json.slice(snapshotTokenIndex + snapshotToken.length + 1);
+            this._state = "parse-snapshot-info";
+        }
+        case "parse-snapshot-info": {
+            var closingBracketIndex = WebInspector.findBalancedCurlyBrackets(this._json);
+            if (closingBracketIndex === -1)
+                return;
+            this._snapshot.snapshot = /** @type {HeapSnapshotHeader} */ (JSON.parse(this._json.slice(0, closingBracketIndex)));
+            this._json = this._json.slice(closingBracketIndex);
+            this._state = "find-nodes";
+        }
+        case "find-nodes": {
+            var nodesToken = "\"nodes\"";
+            var nodesTokenIndex = this._json.indexOf(nodesToken);
+            if (nodesTokenIndex === -1)
+                return;
+            var bracketIndex = this._json.indexOf("[", nodesTokenIndex);
+            if (bracketIndex === -1)
+                return;
+            this._json = this._json.slice(bracketIndex + 1);
+            var node_fields_count = this._snapshot.snapshot.meta.node_fields.length;
+            var nodes_length = this._snapshot.snapshot.node_count * node_fields_count;
+            this._array = new Uint32Array(nodes_length);
+            this._arrayIndex = 0;
+            this._state = "parse-nodes";
+        }
+        case "parse-nodes": {
+            if (this._parseUintArray())
+                return;
+            this._snapshot.nodes = this._array;
+            this._state = "find-edges";
+            this._array = null;
+        }
+        case "find-edges": {
+            var edgesToken = "\"edges\"";
+            var edgesTokenIndex = this._json.indexOf(edgesToken);
+            if (edgesTokenIndex === -1)
+                return;
+            var bracketIndex = this._json.indexOf("[", edgesTokenIndex);
+            if (bracketIndex === -1)
+                return;
+            this._json = this._json.slice(bracketIndex + 1);
+            var edge_fields_count = this._snapshot.snapshot.meta.edge_fields.length;
+            var edges_length = this._snapshot.snapshot.edge_count * edge_fields_count;
+            this._array = new Uint32Array(edges_length);
+            this._arrayIndex = 0;
+            this._state = "parse-edges";
+        }
+        case "parse-edges": {
+            if (this._parseUintArray())
+                return;
+            this._snapshot.edges = this._array;
+            this._array = null;
+            this._state = "find-strings";
+        }
+        case "find-strings": {
+            var stringsToken = "\"strings\"";
+            var stringsTokenIndex = this._json.indexOf(stringsToken);
+            if (stringsTokenIndex === -1)
+                return;
+            var bracketIndex = this._json.indexOf("[", stringsTokenIndex);
+            if (bracketIndex === -1)
+                return;
+            this._json = this._json.slice(bracketIndex);
+            this._state = "accumulate-strings";
+            break;
+        }
+        case "accumulate-strings":
+            break;
+        }
+    }
+};
diff --git a/Source/devtools/front_end/HeapSnapshotProxy.js b/Source/devtools/front_end/HeapSnapshotProxy.js
new file mode 100644
index 0000000..adb2b0f
--- /dev/null
+++ b/Source/devtools/front_end/HeapSnapshotProxy.js
@@ -0,0 +1,570 @@
+/*
+ * 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:
+ *
+ *     * Redistributions of source code must retain the above copyrightdd
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.HeapSnapshotWorkerWrapper = function()
+{
+}
+
+WebInspector.HeapSnapshotWorkerWrapper.prototype =  {
+    postMessage: function(message)
+    {
+    },
+    terminate: function()
+    {
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotWorkerWrapper}
+ */
+WebInspector.HeapSnapshotRealWorker = function()
+{
+    this._worker = new Worker("HeapSnapshotWorker.js");
+    this._worker.addEventListener("message", this._messageReceived.bind(this), false);
+}
+
+WebInspector.HeapSnapshotRealWorker.prototype = {
+    _messageReceived: function(event)
+    {
+        var message = event.data;
+        if ("callId" in message)
+            this.dispatchEventToListeners("message", message);
+        else {
+            if (message.object !== "console") {
+                console.log(WebInspector.UIString("Worker asks to call a method '%s' on an unsupported object '%s'.", message.method, message.object));
+                return;
+            }
+            if (message.method !== "log" && message.method !== "info" && message.method !== "error") {
+                console.log(WebInspector.UIString("Worker asks to call an unsupported method '%s' on the console object.", message.method));
+                return;
+            }
+            console[message.method].apply(window[message.object], message.arguments);
+        }
+    },
+
+    postMessage: function(message)
+    {
+        this._worker.postMessage(message);
+    },
+
+    terminate: function()
+    {
+        this._worker.terminate();
+    },
+
+    __proto__: WebInspector.HeapSnapshotWorkerWrapper.prototype
+}
+
+
+/**
+ * @constructor
+ */
+WebInspector.AsyncTaskQueue = function()
+{
+    this._queue = [];
+    this._isTimerSheduled = false;
+}
+
+WebInspector.AsyncTaskQueue.prototype = {
+    /**
+     * @param {function()} task
+     */
+    addTask: function(task)
+    {
+        this._queue.push(task);
+        this._scheduleTimer();
+    },
+
+    _onTimeout: function()
+    {
+        this._isTimerSheduled = false;
+        var queue = this._queue;
+        this._queue = [];
+        for (var i = 0; i < queue.length; i++) {
+            try {
+                queue[i]();
+            } catch (e) {
+                console.error("Exception while running task: " + e.stack);
+            }
+        }
+        this._scheduleTimer();
+    },
+
+    _scheduleTimer: function()
+    {
+        if (this._queue.length && !this._isTimerSheduled) {
+            setTimeout(this._onTimeout.bind(this), 0);
+            this._isTimerSheduled = true;
+        }
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotWorkerWrapper}
+ */
+WebInspector.HeapSnapshotFakeWorker = function()
+{
+    this._dispatcher = new WebInspector.HeapSnapshotWorkerDispatcher(window, this._postMessageFromWorker.bind(this));
+    this._asyncTaskQueue = new WebInspector.AsyncTaskQueue();
+}
+
+WebInspector.HeapSnapshotFakeWorker.prototype = {
+    postMessage: function(message)
+    {
+        function dispatch()
+        {
+            if (this._dispatcher)
+                this._dispatcher.dispatchMessage({data: message});
+        }
+        this._asyncTaskQueue.addTask(dispatch.bind(this));
+    },
+
+    terminate: function()
+    {
+        this._dispatcher = null;
+    },
+
+    _postMessageFromWorker: function(message)
+    {
+        function send()
+        {
+            this.dispatchEventToListeners("message", message);
+        }
+        this._asyncTaskQueue.addTask(send.bind(this));
+    },
+
+    __proto__: WebInspector.HeapSnapshotWorkerWrapper.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.HeapSnapshotWorker = function()
+{
+    this._nextObjectId = 1;
+    this._nextCallId = 1;
+    this._callbacks = [];
+    this._previousCallbacks = [];
+    // There is no support for workers in Chromium DRT.
+    this._worker = typeof InspectorTest === "undefined" ? new WebInspector.HeapSnapshotRealWorker() : new WebInspector.HeapSnapshotFakeWorker();
+    this._worker.addEventListener("message", this._messageReceived, this);
+}
+
+WebInspector.HeapSnapshotWorker.prototype = {
+    createLoader: function(snapshotConstructorName, proxyConstructor)
+    {
+        var objectId = this._nextObjectId++;
+        var proxy = new WebInspector.HeapSnapshotLoaderProxy(this, objectId, snapshotConstructorName, proxyConstructor);
+        this._postMessage({callId: this._nextCallId++, disposition: "create", objectId: objectId, methodName: "WebInspector.HeapSnapshotLoader"});
+        return proxy;
+    },
+
+    dispose: function()
+    {
+        this._worker.terminate();
+        if (this._interval)
+            clearInterval(this._interval);
+    },
+
+    disposeObject: function(objectId)
+    {
+        this._postMessage({callId: this._nextCallId++, disposition: "dispose", objectId: objectId});
+    },
+
+    callGetter: function(callback, objectId, getterName)
+    {
+        var callId = this._nextCallId++;
+        this._callbacks[callId] = callback;
+        this._postMessage({callId: callId, disposition: "getter", objectId: objectId, methodName: getterName});
+    },
+
+    callFactoryMethod: function(callback, objectId, methodName, proxyConstructor)
+    {
+        var callId = this._nextCallId++;
+        var methodArguments = Array.prototype.slice.call(arguments, 4);
+        var newObjectId = this._nextObjectId++;
+        if (callback) {
+            function wrapCallback(remoteResult)
+            {
+                callback(remoteResult ? new proxyConstructor(this, newObjectId) : null);
+            }
+            this._callbacks[callId] = wrapCallback.bind(this);
+            this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
+            return null;
+        } else {
+            this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
+            return new proxyConstructor(this, newObjectId);
+        }
+    },
+
+    callMethod: function(callback, objectId, methodName)
+    {
+        var callId = this._nextCallId++;
+        var methodArguments = Array.prototype.slice.call(arguments, 3);
+        if (callback)
+            this._callbacks[callId] = callback;
+        this._postMessage({callId: callId, disposition: "method", objectId: objectId, methodName: methodName, methodArguments: methodArguments});
+    },
+
+    startCheckingForLongRunningCalls: function()
+    {
+        if (this._interval)
+            return;
+        this._checkLongRunningCalls();
+        this._interval = setInterval(this._checkLongRunningCalls.bind(this), 300);
+    },
+
+    _checkLongRunningCalls: function()
+    {
+        for (var callId in this._previousCallbacks)
+            if (!(callId in this._callbacks))
+                delete this._previousCallbacks[callId];
+        var hasLongRunningCalls = false;
+        for (callId in this._previousCallbacks) {
+            hasLongRunningCalls = true;
+            break;
+        }
+        this.dispatchEventToListeners("wait", hasLongRunningCalls);
+        for (callId in this._callbacks)
+            this._previousCallbacks[callId] = true;
+    },
+
+    _findFunction: function(name)
+    {
+        var path = name.split(".");
+        var result = window;
+        for (var i = 0; i < path.length; ++i)
+            result = result[path[i]];
+        return result;
+    },
+
+    _messageReceived: function(event)
+    {
+        var data = event.data;
+        if (event.data.error) {
+            if (event.data.errorMethodName)
+                WebInspector.log(WebInspector.UIString("An error happened when a call for method '%s' was requested", event.data.errorMethodName));
+            WebInspector.log(event.data.errorCallStack);
+            delete this._callbacks[data.callId];
+            return;
+        }
+        if (!this._callbacks[data.callId])
+            return;
+        var callback = this._callbacks[data.callId];
+        delete this._callbacks[data.callId];
+        callback(data.result);
+    },
+
+    _postMessage: function(message)
+    {
+        this._worker.postMessage(message);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotProxyObject = function(worker, objectId)
+{
+    this._worker = worker;
+    this._objectId = objectId;
+}
+
+WebInspector.HeapSnapshotProxyObject.prototype = {
+    _callWorker: function(workerMethodName, args)
+    {
+        args.splice(1, 0, this._objectId);
+        return this._worker[workerMethodName].apply(this._worker, args);
+    },
+
+    dispose: function()
+    {
+        this._worker.disposeObject(this._objectId);
+    },
+
+    disposeWorker: function()
+    {
+        this._worker.dispose();
+    },
+
+    /**
+     * @param {...*} var_args
+     */
+    callFactoryMethod: function(callback, methodName, proxyConstructor, var_args)
+    {
+        return this._callWorker("callFactoryMethod", Array.prototype.slice.call(arguments, 0));
+    },
+
+    callGetter: function(callback, getterName)
+    {
+        return this._callWorker("callGetter", Array.prototype.slice.call(arguments, 0));
+    },
+
+    /**
+     * @param {...*} var_args
+     */
+    callMethod: function(callback, methodName, var_args)
+    {
+        return this._callWorker("callMethod", Array.prototype.slice.call(arguments, 0));
+    },
+
+    get worker() {
+        return this._worker;
+    }
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotProxyObject}
+ * @implements {WebInspector.OutputStream}
+ */
+WebInspector.HeapSnapshotLoaderProxy = function(worker, objectId, snapshotConstructorName, proxyConstructor)
+{
+    WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
+    this._snapshotConstructorName = snapshotConstructorName;
+    this._proxyConstructor = proxyConstructor;
+    this._pendingSnapshotConsumers = [];
+}
+
+WebInspector.HeapSnapshotLoaderProxy.prototype = {
+    /**
+     * @param {function(WebInspector.HeapSnapshotProxy)} callback
+     */
+    addConsumer: function(callback)
+    {
+        this._pendingSnapshotConsumers.push(callback);
+    },
+
+    /**
+     * @param {string} chunk
+     * @param {function(WebInspector.OutputStream)=} callback
+     */
+    write: function(chunk, callback)
+    {
+        this.callMethod(callback, "write", chunk);
+    },
+
+    close: function()
+    {
+        function buildSnapshot()
+        {
+            this.callFactoryMethod(updateStaticData.bind(this), "buildSnapshot", this._proxyConstructor, this._snapshotConstructorName);
+        }
+        function updateStaticData(snapshotProxy)
+        {
+            this.dispose();
+            snapshotProxy.updateStaticData(notifyPendingConsumers.bind(this));
+        }
+        function notifyPendingConsumers(snapshotProxy)
+        {
+            for (var i = 0; i < this._pendingSnapshotConsumers.length; ++i)
+                this._pendingSnapshotConsumers[i](snapshotProxy);
+            this._pendingSnapshotConsumers = [];
+        }
+        this.callMethod(buildSnapshot.bind(this), "close");
+    },
+
+    __proto__: WebInspector.HeapSnapshotProxyObject.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotProxyObject}
+ */
+WebInspector.HeapSnapshotProxy = function(worker, objectId)
+{
+    WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
+}
+
+WebInspector.HeapSnapshotProxy.prototype = {
+    aggregates: function(sortedIndexes, key, filter, callback)
+    {
+        this.callMethod(callback, "aggregates", sortedIndexes, key, filter);
+    },
+
+    aggregatesForDiff: function(callback)
+    {
+        this.callMethod(callback, "aggregatesForDiff");
+    },
+
+    calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates, callback)
+    {
+        this.callMethod(callback, "calculateSnapshotDiff", baseSnapshotId, baseSnapshotAggregates);
+    },
+
+    nodeClassName: function(snapshotObjectId, callback)
+    {
+        this.callMethod(callback, "nodeClassName", snapshotObjectId);
+    },
+
+    dominatorIdsForNode: function(nodeIndex, callback)
+    {
+        this.callMethod(callback, "dominatorIdsForNode", nodeIndex);
+    },
+
+    createEdgesProvider: function(nodeIndex, showHiddenData)
+    {
+        return this.callFactoryMethod(null, "createEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex, showHiddenData);
+    },
+
+    createRetainingEdgesProvider: function(nodeIndex, showHiddenData)
+    {
+        return this.callFactoryMethod(null, "createRetainingEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex, showHiddenData);
+    },
+
+    createAddedNodesProvider: function(baseSnapshotId, className)
+    {
+        return this.callFactoryMethod(null, "createAddedNodesProvider", WebInspector.HeapSnapshotProviderProxy, baseSnapshotId, className);
+    },
+
+    createDeletedNodesProvider: function(nodeIndexes)
+    {
+        return this.callFactoryMethod(null, "createDeletedNodesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndexes);
+    },
+
+    createNodesProvider: function(filter)
+    {
+        return this.callFactoryMethod(null, "createNodesProvider", WebInspector.HeapSnapshotProviderProxy, filter);
+    },
+
+    createNodesProviderForClass: function(className, aggregatesKey)
+    {
+        return this.callFactoryMethod(null, "createNodesProviderForClass", WebInspector.HeapSnapshotProviderProxy, className, aggregatesKey);
+    },
+
+    createNodesProviderForDominator: function(nodeIndex)
+    {
+        return this.callFactoryMethod(null, "createNodesProviderForDominator", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
+    },
+
+    dispose: function()
+    {
+        this.disposeWorker();
+    },
+
+    get nodeCount()
+    {
+        return this._staticData.nodeCount;
+    },
+
+    get rootNodeIndex()
+    {
+        return this._staticData.rootNodeIndex;
+    },
+
+    updateStaticData: function(callback)
+    {
+        function dataReceived(staticData)
+        {
+            this._staticData = staticData;
+            callback(this);
+        }
+        this.callMethod(dataReceived.bind(this), "updateStaticData");
+    },
+
+    get totalSize()
+    {
+        return this._staticData.totalSize;
+    },
+
+    get uid()
+    {
+        return this._staticData.uid;
+    },
+
+    __proto__: WebInspector.HeapSnapshotProxyObject.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotProxy}
+ */
+WebInspector.NativeHeapSnapshotProxy = function(worker, objectId)
+{
+    WebInspector.HeapSnapshotProxy.call(this, worker, objectId);
+}
+
+WebInspector.NativeHeapSnapshotProxy.prototype = {
+    images: function(callback)
+    {
+        this.callMethod(callback, "images");
+    },
+
+    __proto__: WebInspector.HeapSnapshotProxy.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotProxyObject}
+ */
+WebInspector.HeapSnapshotProviderProxy = function(worker, objectId)
+{
+    WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
+}
+
+WebInspector.HeapSnapshotProviderProxy.prototype = {
+    nodePosition: function(snapshotObjectId, callback)
+    {
+        this.callMethod(callback, "nodePosition", snapshotObjectId);
+    },
+
+    isEmpty: function(callback)
+    {
+        this.callMethod(callback, "isEmpty");
+    },
+
+    serializeItemsRange: function(startPosition, endPosition, callback)
+    {
+        this.callMethod(callback, "serializeItemsRange", startPosition, endPosition);
+    },
+
+    sortAndRewind: function(comparator, callback)
+    {
+        this.callMethod(callback, "sortAndRewind", comparator);
+    },
+
+    __proto__: WebInspector.HeapSnapshotProxyObject.prototype
+}
+
diff --git a/Source/devtools/front_end/HeapSnapshotView.js b/Source/devtools/front_end/HeapSnapshotView.js
new file mode 100644
index 0000000..71a100b
--- /dev/null
+++ b/Source/devtools/front_end/HeapSnapshotView.js
@@ -0,0 +1,1573 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {!WebInspector.ProfilesPanel} parent
+ * @param {!WebInspector.HeapProfileHeader} profile
+ */
+WebInspector.HeapSnapshotView = function(parent, profile)
+{
+    WebInspector.View.call(this);
+
+    this.element.addStyleClass("heap-snapshot-view");
+
+    this.parent = parent;
+    this.parent.addEventListener("profile added", this._onProfileHeaderAdded, this);
+
+    if (profile._profileSamples) {
+        this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile);
+        this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
+        this._trackingOverviewGrid.show(this.element);
+    }
+
+    this.viewsContainer = document.createElement("div");
+    this.viewsContainer.addStyleClass("views-container");
+    this.element.appendChild(this.viewsContainer);
+
+    this.containmentView = new WebInspector.View();
+    this.containmentView.element.addStyleClass("view");
+    this.containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid();
+    this.containmentDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
+    this.containmentDataGrid.show(this.containmentView.element);
+    this.containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
+
+    this.constructorsView = new WebInspector.View();
+    this.constructorsView.element.addStyleClass("view");
+    this.constructorsView.element.appendChild(this._createToolbarWithClassNameFilter());
+
+    this.constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid();
+    this.constructorsDataGrid.element.addStyleClass("class-view-grid");
+    this.constructorsDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
+    this.constructorsDataGrid.show(this.constructorsView.element);
+    this.constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
+
+    this.diffView = new WebInspector.View();
+    this.diffView.element.addStyleClass("view");
+    this.diffView.element.appendChild(this._createToolbarWithClassNameFilter());
+
+    this.diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid();
+    this.diffDataGrid.element.addStyleClass("class-view-grid");
+    this.diffDataGrid.show(this.diffView.element);
+    this.diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
+
+    this.dominatorView = new WebInspector.View();
+    this.dominatorView.element.addStyleClass("view");
+    this.dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid();
+    this.dominatorDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
+    this.dominatorDataGrid.show(this.dominatorView.element);
+    this.dominatorDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
+
+    this.retainmentViewHeader = document.createElement("div");
+    this.retainmentViewHeader.addStyleClass("retainers-view-header");
+    WebInspector.installDragHandle(this.retainmentViewHeader, this._startRetainersHeaderDragging.bind(this), this._retainersHeaderDragging.bind(this), this._endRetainersHeaderDragging.bind(this), "row-resize");
+    var retainingPathsTitleDiv = document.createElement("div");
+    retainingPathsTitleDiv.className = "title";
+    var retainingPathsTitle = document.createElement("span");
+    retainingPathsTitle.textContent = WebInspector.UIString("Object's retaining tree");
+    retainingPathsTitleDiv.appendChild(retainingPathsTitle);
+    this.retainmentViewHeader.appendChild(retainingPathsTitleDiv);
+    this.element.appendChild(this.retainmentViewHeader);
+
+    this.retainmentView = new WebInspector.View();
+    this.retainmentView.element.addStyleClass("view");
+    this.retainmentView.element.addStyleClass("retaining-paths-view");
+    this.retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid();
+    this.retainmentDataGrid.show(this.retainmentView.element);
+    this.retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
+    this.retainmentView.show(this.element);
+    this.retainmentDataGrid.reset();
+
+    this.dataGrid = /** @type {WebInspector.HeapSnapshotSortableDataGrid} */ (this.constructorsDataGrid);
+    this.currentView = this.constructorsView;
+
+    this.viewSelect = new WebInspector.StatusBarComboBox(this._onSelectedViewChanged.bind(this));
+
+    this.views = [{title: "Summary", view: this.constructorsView, grid: this.constructorsDataGrid},
+                  {title: "Comparison", view: this.diffView, grid: this.diffDataGrid},
+                  {title: "Containment", view: this.containmentView, grid: this.containmentDataGrid},
+                  {title: "Dominators", view: this.dominatorView, grid: this.dominatorDataGrid}];
+    this.views.current = 0;
+    for (var i = 0; i < this.views.length; ++i)
+        this.viewSelect.createOption(WebInspector.UIString(this.views[i].title));
+
+    this._profileUid = profile.uid;
+    this._profileTypeId = profile.profileType().id;
+
+    this.baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this));
+    this.baseSelect.element.addStyleClass("hidden");
+    this._updateBaseOptions();
+
+    this.filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this));
+    this._updateFilterOptions();
+
+    this.helpButton = new WebInspector.StatusBarButton("", "heap-snapshot-help-status-bar-item status-bar-item");
+    this.helpButton.addEventListener("click", this._helpClicked, this);
+
+    this.selectedSizeText = new WebInspector.StatusBarText("");
+
+    this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
+
+    this.profile.load(profileCallback.bind(this));
+
+    function profileCallback(heapSnapshotProxy)
+    {
+        var list = this._profiles();
+        var profileIndex;
+        for (var i = 0; i < list.length; ++i) {
+            if (list[i].uid === this._profileUid) {
+                profileIndex = i;
+                break;
+            }
+        }
+
+        if (profileIndex > 0)
+            this.baseSelect.setSelectedIndex(profileIndex - 1);
+        else
+            this.baseSelect.setSelectedIndex(profileIndex);
+        this.dataGrid.setDataSource(heapSnapshotProxy);
+    }
+}
+
+WebInspector.HeapSnapshotView.prototype = {
+    _onIdsRangeChanged: function(event)
+    {
+        var minId = event.data.minId;
+        var maxId = event.data.maxId;
+        this.selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
+        if (this.constructorsDataGrid._minNodeId !== minId || this.constructorsDataGrid._maxNodeId !== maxId) {
+            // FIXME(loislo): we should implement rangeFilter method in constructorsDataGrid.
+            this.constructorsDataGrid._minNodeId = minId;
+            this.constructorsDataGrid._maxNodeId = maxId;
+            if (this.constructorsDataGrid.snapshot) {
+                this.constructorsDataGrid._profileIndex = 1;
+                this.constructorsDataGrid._populateChildren();
+            }
+        }
+    },
+
+    dispose: function()
+    {
+        this.profile.dispose();
+        if (this.baseProfile)
+            this.baseProfile.dispose();
+        this.containmentDataGrid.dispose();
+        this.constructorsDataGrid.dispose();
+        this.diffDataGrid.dispose();
+        this.dominatorDataGrid.dispose();
+        this.retainmentDataGrid.dispose();
+    },
+
+    get statusBarItems()
+    {
+        return [this.viewSelect.element, this.baseSelect.element, this.filterSelect.element, this.helpButton.element, this.selectedSizeText.element];
+    },
+
+    get profile()
+    {
+        return this.parent.getProfile(this._profileTypeId, this._profileUid);
+    },
+
+    get baseProfile()
+    {
+        return this.parent.getProfile(this._profileTypeId, this._baseProfileUid);
+    },
+
+    wasShown: function()
+    {
+        // FIXME: load base and current snapshots in parallel
+        this.profile.load(profileCallback1.bind(this));
+
+        function profileCallback1() {
+            if (this.baseProfile)
+                this.baseProfile.load(profileCallback2.bind(this));
+            else
+                profileCallback2.call(this);
+        }
+
+        function profileCallback2() {
+            this.currentView.show(this.viewsContainer);
+        }
+    },
+
+    willHide: function()
+    {
+        this._currentSearchResultIndex = -1;
+        this._popoverHelper.hidePopover();
+        if (this.helpPopover && this.helpPopover.isShowing())
+            this.helpPopover.hide();
+    },
+
+    onResize: function()
+    {
+        var height = this.retainmentView.element.clientHeight;
+        this._updateRetainmentViewHeight(height);
+    },
+
+    searchCanceled: function()
+    {
+        if (this._searchResults) {
+            for (var i = 0; i < this._searchResults.length; ++i) {
+                var node = this._searchResults[i].node;
+                delete node._searchMatched;
+                node.refresh();
+            }
+        }
+
+        delete this._searchFinishedCallback;
+        this._currentSearchResultIndex = -1;
+        this._searchResults = [];
+    },
+
+    performSearch: function(query, finishedCallback)
+    {
+        // Call searchCanceled since it will reset everything we need before doing a new search.
+        this.searchCanceled();
+
+        query = query.trim();
+
+        if (!query.length)
+            return;
+        if (this.currentView !== this.constructorsView && this.currentView !== this.diffView)
+            return;
+
+        this._searchFinishedCallback = finishedCallback;
+
+        function matchesByName(gridNode) {
+            return ("_name" in gridNode) && gridNode._name.hasSubstring(query, true);
+        }
+
+        function matchesById(gridNode) {
+            return ("snapshotNodeId" in gridNode) && gridNode.snapshotNodeId === query;
+        }
+
+        var matchPredicate;
+        if (query.charAt(0) !== "@")
+            matchPredicate = matchesByName;
+        else {
+            query = parseInt(query.substring(1), 10);
+            matchPredicate = matchesById;
+        }
+
+        function matchesQuery(gridNode)
+        {
+            delete gridNode._searchMatched;
+            if (matchPredicate(gridNode)) {
+                gridNode._searchMatched = true;
+                gridNode.refresh();
+                return true;
+            }
+            return false;
+        }
+
+        var current = this.dataGrid.rootNode().children[0];
+        var depth = 0;
+        var info = {};
+
+        // Restrict to type nodes and instances.
+        const maxDepth = 1;
+
+        while (current) {
+            if (matchesQuery(current))
+                this._searchResults.push({ node: current });
+            current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
+            depth += info.depthChange;
+        }
+
+        finishedCallback(this, this._searchResults.length);
+    },
+
+    jumpToFirstSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        this._currentSearchResultIndex = 0;
+        this._jumpToSearchResult(this._currentSearchResultIndex);
+    },
+
+    jumpToLastSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        this._currentSearchResultIndex = (this._searchResults.length - 1);
+        this._jumpToSearchResult(this._currentSearchResultIndex);
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        if (++this._currentSearchResultIndex >= this._searchResults.length)
+            this._currentSearchResultIndex = 0;
+        this._jumpToSearchResult(this._currentSearchResultIndex);
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        if (!this._searchResults || !this._searchResults.length)
+            return;
+        if (--this._currentSearchResultIndex < 0)
+            this._currentSearchResultIndex = (this._searchResults.length - 1);
+        this._jumpToSearchResult(this._currentSearchResultIndex);
+    },
+
+    showingFirstSearchResult: function()
+    {
+        return (this._currentSearchResultIndex === 0);
+    },
+
+    showingLastSearchResult: function()
+    {
+        return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
+    },
+
+    _jumpToSearchResult: function(index)
+    {
+        var searchResult = this._searchResults[index];
+        if (!searchResult)
+            return;
+
+        var node = searchResult.node;
+        node.revealAndSelect();
+    },
+
+    refreshVisibleData: function()
+    {
+        var child = this.dataGrid.rootNode().children[0];
+        while (child) {
+            child.refresh();
+            child = child.traverseNextNode(false, null, true);
+        }
+    },
+
+    _changeBase: function()
+    {
+        if (this._baseProfileUid === this._profiles()[this.baseSelect.selectedIndex()].uid)
+            return;
+
+        this._baseProfileUid = this._profiles()[this.baseSelect.selectedIndex()].uid;
+        var dataGrid = /** @type {WebInspector.HeapSnapshotDiffDataGrid} */ (this.dataGrid);
+        // Change set base data source only if main data source is already set.
+        if (dataGrid.snapshot)
+            this.baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid));
+
+        if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
+            return;
+
+        // The current search needs to be performed again. First negate out previous match
+        // count by calling the search finished callback with a negative number of matches.
+        // Then perform the search again with the same query and callback.
+        this._searchFinishedCallback(this, -this._searchResults.length);
+        this.performSearch(this.currentQuery, this._searchFinishedCallback);
+    },
+
+    _changeFilter: function()
+    {
+        var profileIndex = this.filterSelect.selectedIndex() - 1;
+        this.dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged,
+            label: this.filterSelect.selectedOption().label
+        });
+
+        if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
+            return;
+
+        // The current search needs to be performed again. First negate out previous match
+        // count by calling the search finished callback with a negative number of matches.
+        // Then perform the search again with the same query and callback.
+        this._searchFinishedCallback(this, -this._searchResults.length);
+        this.performSearch(this.currentQuery, this._searchFinishedCallback);
+    },
+
+    _createToolbarWithClassNameFilter: function()
+    {
+        var toolbar = document.createElement("div");
+        toolbar.addStyleClass("class-view-toolbar");
+        var classNameFilter = document.createElement("input");
+        classNameFilter.addStyleClass("class-name-filter");
+        classNameFilter.setAttribute("placeholder", WebInspector.UIString("Class filter"));
+        classNameFilter.addEventListener("keyup", this._changeNameFilter.bind(this, classNameFilter), false);
+        toolbar.appendChild(classNameFilter);
+        return toolbar;
+    },
+
+    _changeNameFilter: function(classNameInputElement)
+    {
+        var filter = classNameInputElement.value;
+        this.dataGrid.changeNameFilter(filter);
+    },
+
+    /**
+     * @return {!Array.<!WebInspector.ProfileHeader>}
+     */
+    _profiles: function()
+    {
+        return this.parent.getProfileType(this._profileTypeId).getProfiles();
+    },
+
+    /**
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Event} event
+     */
+    populateContextMenu: function(contextMenu, event)
+    {
+        this.dataGrid.populateContextMenu(this.parent, contextMenu, event);
+    },
+
+    _selectionChanged: function(event)
+    {
+        var selectedNode = event.target.selectedNode;
+        this._setRetainmentDataGridSource(selectedNode);
+        this._inspectedObjectChanged(event);
+    },
+
+    _inspectedObjectChanged: function(event)
+    {
+        var selectedNode = event.target.selectedNode;
+        if (!this.profile.fromFile() && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
+            ConsoleAgent.addInspectedHeapObject(selectedNode.snapshotNodeId);
+    },
+
+    _setRetainmentDataGridSource: function(nodeItem)
+    {
+        if (nodeItem && nodeItem.snapshotNodeIndex)
+            this.retainmentDataGrid.setDataSource(nodeItem.isDeletedNode ? nodeItem.dataGrid.baseSnapshot : nodeItem.dataGrid.snapshot, nodeItem.snapshotNodeIndex);
+        else
+            this.retainmentDataGrid.reset();
+    },
+
+    _mouseDownInContentsGrid: function(event)
+    {
+        if (event.detail < 2)
+            return;
+
+        var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
+        if (!cell || (!cell.hasStyleClass("count-column") && !cell.hasStyleClass("shallowSize-column") && !cell.hasStyleClass("retainedSize-column")))
+            return;
+
+        event.consume(true);
+    },
+
+    changeView: function(viewTitle, callback)
+    {
+        var viewIndex = null;
+        for (var i = 0; i < this.views.length; ++i) {
+            if (this.views[i].title === viewTitle) {
+                viewIndex = i;
+                break;
+            }
+        }
+        if (this.views.current === viewIndex || viewIndex == null) {
+            setTimeout(callback, 0);
+            return;
+        }
+
+        function dataGridContentShown(event)
+        {
+            var dataGrid = event.data;
+            dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
+            if (dataGrid === this.dataGrid)
+                callback();
+        }
+        this.views[viewIndex].grid.addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
+
+        this.viewSelect.setSelectedIndex(viewIndex);
+        this._changeView(viewIndex);
+    },
+
+    _updateDataSourceAndView: function()
+    {
+        var dataGrid = this.dataGrid;
+        if (dataGrid.snapshot)
+            return;
+
+        this.profile.load(didLoadSnapshot.bind(this));
+        function didLoadSnapshot(snapshotProxy)
+        {
+            if (this.dataGrid !== dataGrid)
+                return;
+            if (dataGrid.snapshot !== snapshotProxy)
+                dataGrid.setDataSource(snapshotProxy);
+            if (dataGrid === this.diffDataGrid) {
+                if (!this._baseProfileUid)
+                    this._baseProfileUid = this._profiles()[this.baseSelect.selectedIndex()].uid;
+                this.baseProfile.load(didLoadBaseSnaphot.bind(this));
+            }
+        }
+
+        function didLoadBaseSnaphot(baseSnapshotProxy)
+        {
+            if (this.diffDataGrid.baseSnapshot !== baseSnapshotProxy)
+                this.diffDataGrid.setBaseDataSource(baseSnapshotProxy);
+        }
+    },
+
+    _onSelectedViewChanged: function(event)
+    {
+        this._changeView(event.target.selectedIndex);
+    },
+
+    _updateSelectorsVisibility: function()
+    {
+        if (this.currentView === this.diffView)
+            this.baseSelect.element.removeStyleClass("hidden");
+        else
+            this.baseSelect.element.addStyleClass("hidden");
+
+        if (this.currentView === this.constructorsView) {
+            if (this._trackingOverviewGrid) {
+                this._trackingOverviewGrid.element.removeStyleClass("hidden");
+                this._trackingOverviewGrid.update(true);
+                this.viewsContainer.addStyleClass("reserve-80px-at-top");
+            }
+            this.filterSelect.element.removeStyleClass("hidden");
+        } else {
+            this.filterSelect.element.addStyleClass("hidden");
+            if (this._trackingOverviewGrid) {
+                this._trackingOverviewGrid.element.addStyleClass("hidden");
+                this.viewsContainer.removeStyleClass("reserve-80px-at-top");
+            }
+        }
+    },
+
+    _changeView: function(selectedIndex)
+    {
+        if (selectedIndex === this.views.current)
+            return;
+
+        this.views.current = selectedIndex;
+        this.currentView.detach();
+        var view = this.views[this.views.current];
+        this.currentView = view.view;
+        this.dataGrid = view.grid;
+        this.currentView.show(this.viewsContainer);
+        this.refreshVisibleData();
+        this.dataGrid.updateWidths();
+
+        this._updateSelectorsVisibility();
+
+        this._updateDataSourceAndView();
+
+        if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
+            return;
+
+        // The current search needs to be performed again. First negate out previous match
+        // count by calling the search finished callback with a negative number of matches.
+        // Then perform the search again the with same query and callback.
+        this._searchFinishedCallback(this, -this._searchResults.length);
+        this.performSearch(this.currentQuery, this._searchFinishedCallback);
+    },
+
+    _getHoverAnchor: function(target)
+    {
+        var span = target.enclosingNodeOrSelfWithNodeName("span");
+        if (!span)
+            return;
+        var row = target.enclosingNodeOrSelfWithNodeName("tr");
+        if (!row)
+            return;
+        span.node = row._dataGridNode;
+        return span;
+    },
+
+    _resolveObjectForPopover: function(element, showCallback, objectGroupName)
+    {
+        if (this.profile.fromFile())
+            return;
+        element.node.queryObjectContent(showCallback, objectGroupName);
+    },
+
+    _helpClicked: function(event)
+    {
+        if (!this._helpPopoverContentElement) {
+            var refTypes = ["a:", "console-formatted-name", WebInspector.UIString("property"),
+                            "0:", "console-formatted-name", WebInspector.UIString("element"),
+                            "a:", "console-formatted-number", WebInspector.UIString("context var"),
+                            "a:", "console-formatted-null", WebInspector.UIString("system prop")];
+            var objTypes = [" a ", "console-formatted-object", "Object",
+                            "\"a\"", "console-formatted-string", "String",
+                            "/a/", "console-formatted-string", "RegExp",
+                            "a()", "console-formatted-function", "Function",
+                            "a[]", "console-formatted-object", "Array",
+                            "num", "console-formatted-number", "Number",
+                            " a ", "console-formatted-null", "System"];
+
+            var contentElement = document.createElement("table");
+            contentElement.className = "heap-snapshot-help";
+            var headerRow = document.createElement("tr");
+            var propsHeader = document.createElement("th");
+            propsHeader.textContent = WebInspector.UIString("Property types:");
+            headerRow.appendChild(propsHeader);
+            var objsHeader = document.createElement("th");
+            objsHeader.textContent = WebInspector.UIString("Object types:");
+            headerRow.appendChild(objsHeader);
+            contentElement.appendChild(headerRow);
+
+            function appendHelp(help, index, cell)
+            {
+                var div = document.createElement("div");
+                div.className = "source-code event-properties";
+                var name = document.createElement("span");
+                name.textContent = help[index];
+                name.className = help[index + 1];
+                div.appendChild(name);
+                var desc = document.createElement("span");
+                desc.textContent = " " + help[index + 2];
+                div.appendChild(desc);
+                cell.appendChild(div);
+            }
+
+            var len = Math.max(refTypes.length, objTypes.length);
+            for (var i = 0; i < len; i += 3) {
+                var row = document.createElement("tr");
+                var refCell = document.createElement("td");
+                if (refTypes[i])
+                    appendHelp(refTypes, i, refCell);
+                row.appendChild(refCell);
+                var objCell = document.createElement("td");
+                if (objTypes[i])
+                    appendHelp(objTypes, i, objCell);
+                row.appendChild(objCell);
+                contentElement.appendChild(row);
+            }
+            this._helpPopoverContentElement = contentElement;
+            this.helpPopover = new WebInspector.Popover();
+        }
+        if (this.helpPopover.isShowing())
+            this.helpPopover.hide();
+        else
+            this.helpPopover.show(this._helpPopoverContentElement, this.helpButton.element);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _startRetainersHeaderDragging: function(event)
+    {
+        if (!this.isShowing())
+            return false;
+
+        this._previousDragPosition = event.pageY;
+        return true;
+    },
+
+    _retainersHeaderDragging: function(event)
+    {
+        var height = this.retainmentView.element.clientHeight;
+        height += this._previousDragPosition - event.pageY;
+        this._previousDragPosition = event.pageY;
+        this._updateRetainmentViewHeight(height);
+        event.consume(true);
+    },
+
+    _endRetainersHeaderDragging: function(event)
+    {
+        delete this._previousDragPosition;
+        event.consume();
+    },
+
+    _updateRetainmentViewHeight: function(height)
+    {
+        height = Number.constrain(height, Preferences.minConsoleHeight, this.element.clientHeight - Preferences.minConsoleHeight);
+        this.viewsContainer.style.bottom = (height + this.retainmentViewHeader.clientHeight) + "px";
+        if (this._trackingOverviewGrid && this.currentView === this.constructorsView)
+            this.viewsContainer.addStyleClass("reserve-80px-at-top");
+        this.retainmentView.element.style.height = height + "px";
+        this.retainmentViewHeader.style.bottom = height + "px";
+        this.currentView.doResize();
+    },
+
+    _updateBaseOptions: function()
+    {
+        var list = this._profiles();
+        // We're assuming that snapshots can only be added.
+        if (this.baseSelect.size() === list.length)
+            return;
+
+        for (var i = this.baseSelect.size(), n = list.length; i < n; ++i) {
+            var title = list[i].title;
+            if (WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(title))
+                title = WebInspector.UIString("Snapshot %d", WebInspector.ProfilesPanelDescriptor.userInitiatedProfileIndex(title));
+            this.baseSelect.createOption(title);
+        }
+    },
+
+    _updateFilterOptions: function()
+    {
+        var list = this._profiles();
+        // We're assuming that snapshots can only be added.
+        if (this.filterSelect.size() - 1 === list.length)
+            return;
+
+        if (!this.filterSelect.size())
+            this.filterSelect.createOption(WebInspector.UIString("All objects"));
+
+        if (this.profile.fromFile())
+            return;
+        for (var i = this.filterSelect.size() - 1, n = list.length; i < n; ++i) {
+            var profile = list[i];
+            var title = list[i].title;
+            if (WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(title)) {
+                var profileIndex = WebInspector.ProfilesPanelDescriptor.userInitiatedProfileIndex(title);
+                if (!i)
+                    title = WebInspector.UIString("Objects allocated before Snapshot %d", profileIndex);
+                else
+                    title = WebInspector.UIString("Objects allocated between Snapshots %d and %d", profileIndex - 1, profileIndex);
+            }
+            this.filterSelect.createOption(title);
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onProfileHeaderAdded: function(event)
+    {
+        if (!event.data || event.data.type !== this._profileTypeId)
+            return;
+        this._updateBaseOptions();
+        this._updateFilterOptions();
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileType}
+ * @implements {HeapProfilerAgent.Dispatcher}
+ */
+WebInspector.HeapSnapshotProfileType = function()
+{
+    WebInspector.ProfileType.call(this, WebInspector.HeapSnapshotProfileType.TypeId, WebInspector.UIString("Take Heap Snapshot"));
+    InspectorBackend.registerHeapProfilerDispatcher(this);
+}
+
+WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
+
+WebInspector.HeapSnapshotProfileType.prototype = {
+    /**
+     * @override
+     * @return {string}
+     */
+    fileExtension: function()
+    {
+        return ".heapsnapshot";
+    },
+
+    get buttonTooltip()
+    {
+        return WebInspector.UIString("Take heap snapshot.");
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    isInstantProfile: function()
+    {
+        return true;
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    buttonClicked: function()
+    {
+        this._takeHeapSnapshot();
+        return false;
+    },
+
+    startRecordingProfile: function()
+    {
+        this._lastSeenIndex = -1;
+        this._profileSamples = {
+            'sizes': [],
+            'ids': [],
+            'timestamps': [],
+            'max': []
+        };
+        this._recording = true;
+        this._currentIndex = 0;
+        HeapProfilerAgent.startTrackingHeapObjects();
+    },
+
+    stopRecordingProfile: function()
+    {
+        HeapProfilerAgent.stopTrackingHeapObjects();
+        this.addProfile(this.createTemporaryProfile());
+        HeapProfilerAgent.takeHeapSnapshot(true);
+        this._recording = false;
+    },
+
+    toggleRecording: function()
+    {
+        if (this._recording)
+            this.stopRecordingProfile();
+        else
+            this.startRecordingProfile();
+        return this._recording;
+    },
+
+    /**
+     * @override
+     * @param {Array.<number>} samples
+     */
+    heapStatsUpdate: function(samples)
+    {
+        if (!this._profileSamples) {
+            HeapProfilerAgent.stopTrackingHeapObjects();
+            return;
+        }
+        var index;
+        for (var i = 0; i < samples.length; i += 3) {
+            index = samples[i];
+            var count = samples[i+1];
+            var size  = samples[i+2];
+            this._profileSamples.sizes[index] = size;
+            if (size > this._profileSamples.max[index])
+                this._profileSamples.max[index] = size;
+        }
+        this._lastUpdatedIndex = index;
+    },
+
+    /**
+     * @override
+     * @param {number} lastSeenObjectId
+     * @param {number} timestamp
+     */
+    lastSeenObjectId: function(lastSeenObjectId, timestamp)
+    {
+        this._profileSamples.ids[this._currentIndex] = lastSeenObjectId;
+        this._profileSamples.timestamps[this._currentIndex] = timestamp;
+        if (!this._profileSamples.max[this._currentIndex]) {
+            this._profileSamples.max[this._currentIndex] = 0;
+            this._profileSamples.sizes[this._currentIndex] = 0;
+        }
+        ++this._currentIndex;
+    },
+
+    get  treeItemTitle()
+    {
+        return WebInspector.UIString("HEAP SNAPSHOTS");
+    },
+
+    get description()
+    {
+        return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
+    },
+
+    /**
+     * @override
+     * @param {string=} title
+     * @return {!WebInspector.ProfileHeader}
+     */
+    createTemporaryProfile: function(title)
+    {
+        title = title || WebInspector.UIString("Snapshotting\u2026");
+        return new WebInspector.HeapProfileHeader(this, title);
+    },
+
+    /**
+     * @override
+     * @param {HeapProfilerAgent.ProfileHeader} profile
+     * @return {!WebInspector.ProfileHeader}
+     */
+    createProfile: function(profile)
+    {
+        return new WebInspector.HeapProfileHeader(this, profile.title, profile.uid, profile.maxJSObjectId || 0);
+    },
+
+    _takeHeapSnapshot: function()
+    {
+        var temporaryProfile = this.findTemporaryProfile();
+        if (!temporaryProfile)
+            this.addProfile(this.createTemporaryProfile());
+        HeapProfilerAgent.takeHeapSnapshot(true, function() {});
+        WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
+    },
+
+    /**
+     * @param {HeapProfilerAgent.ProfileHeader} profileHeader
+     */
+    addProfileHeader: function(profileHeader)
+    {
+        var profile = this.createProfile(profileHeader);
+        profile._profileSamples = this._profileSamples;
+        this._profileSamples = null;
+        this.addProfile(profile);
+    },
+
+    /**
+     * @override
+     * @param {number} uid
+     * @param {string} chunk
+     */
+    addHeapSnapshotChunk: function(uid, chunk)
+    {
+        var profile = this._profilesIdMap[this._makeKey(uid)];
+        if (profile)
+            profile.transferChunk(chunk);
+    },
+
+    /**
+     * @override
+     * @param {number} uid
+     */
+    finishHeapSnapshot: function(uid)
+    {
+        var profile = this._profilesIdMap[this._makeKey(uid)];
+        if (profile)
+            profile.finishHeapSnapshot();
+    },
+
+    /**
+     * @override
+     * @param {number} done
+     * @param {number} total
+     */
+    reportHeapSnapshotProgress: function(done, total)
+    {
+        var profile = this.findTemporaryProfile();
+        if (profile)
+            this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProgressUpdated, {"profile": profile, "done": done, "total": total});
+    },
+
+    /**
+     * @override
+     */
+    resetProfiles: function()
+    {
+        this._reset();
+    },
+
+    /**
+     * @override
+     * @param {!WebInspector.ProfileHeader} profile
+     */
+    removeProfile: function(profile)
+    {
+        WebInspector.ProfileType.prototype.removeProfile.call(this, profile);
+        if (!profile.isTemporary)
+            HeapProfilerAgent.removeProfile(profile.uid);
+    },
+
+    /**
+     * @override
+     * @param {function(this:WebInspector.ProfileType, ?string, Array.<HeapProfilerAgent.ProfileHeader>)} populateCallback
+     */
+    _requestProfilesFromBackend: function(populateCallback)
+    {
+        HeapProfilerAgent.getProfileHeaders(populateCallback);
+    },
+
+    __proto__: WebInspector.ProfileType.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileType}
+ * @param {WebInspector.ProfilesPanel} profilesPanel
+ * @param {WebInspector.HeapSnapshotProfileType} profileType
+ */
+WebInspector.TrackingHeapSnapshotProfileType = function(profilesPanel, profileType)
+{
+    WebInspector.ProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Track Allocations"));
+    this._profilesPanel = profilesPanel;
+    this._parentType = profileType;
+}
+
+WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
+
+WebInspector.TrackingHeapSnapshotProfileType.prototype = {
+    get buttonTooltip()
+    {
+        return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    isInstantProfile: function()
+    {
+        return false;
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    buttonClicked: function()
+    {
+        var profile = this.findTemporaryProfile();
+        var result = this._parentType.toggleRecording();
+        if (!result && profile)
+            this._profilesPanel._removeProfileHeader(profile);
+        return result;
+    },
+
+    get treeItemTitle()
+    {
+        return WebInspector.UIString("TRACK HEAP SNAPSHOTS");
+    },
+
+    get description()
+    {
+        return WebInspector.UIString("Run heap profiler continuously to track JavaScript allocations over time.");
+    },
+
+    /**
+     * @override
+     * @param {string=} title
+     * @return {!WebInspector.ProfileHeader}
+     */
+    createTemporaryProfile: function(title)
+    {
+        title = title || WebInspector.UIString("Recording\u2026");
+        return new WebInspector.HeapProfileHeader(this, title);
+    },
+
+    /**
+     * @override
+     */
+    resetProfiles: function()
+    {
+        this._reset();
+    },
+
+    __proto__: WebInspector.ProfileType.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileHeader}
+ * @param {!WebInspector.ProfileType} type
+ * @param {string} title
+ * @param {number=} uid
+ * @param {number=} maxJSObjectId
+ */
+WebInspector.HeapProfileHeader = function(type, title, uid, maxJSObjectId)
+{
+    WebInspector.ProfileHeader.call(this, type, title, uid);
+    this.maxJSObjectId = maxJSObjectId;
+    /**
+     * @type {WebInspector.OutputStream}
+     */
+    this._receiver = null;
+    /**
+     * @type {WebInspector.HeapSnapshotProxy}
+     */
+    this._snapshotProxy = null;
+    this._totalNumberOfChunks = 0;
+}
+
+WebInspector.HeapProfileHeader.prototype = {
+    /**
+     * @override
+     */
+    createSidebarTreeElement: function()
+    {
+        return new WebInspector.ProfileSidebarTreeElement(this, WebInspector.UIString("Snapshot %d"), "heap-snapshot-sidebar-tree-item");
+    },
+
+    /**
+     * @override
+     * @param {!WebInspector.ProfilesPanel} profilesPanel
+     */
+    createView: function(profilesPanel)
+    {
+        return new WebInspector.HeapSnapshotView(profilesPanel, this);
+    },
+
+    /**
+     * @override
+     * @param {function(WebInspector.HeapSnapshotProxy):void} callback
+     */
+    load: function(callback)
+    {
+        if (this._snapshotProxy) {
+            callback(this._snapshotProxy);
+            return;
+        }
+
+        this._numberOfChunks = 0;
+        this._savedChunks = 0;
+        this._savingToFile = false;
+        if (!this._receiver) {
+            this._setupWorker();
+            this.sidebarElement.subtitle = WebInspector.UIString("Loading\u2026");
+            this.sidebarElement.wait = true;
+            this.startSnapshotTransfer();
+        }
+        var loaderProxy = /** @type {WebInspector.HeapSnapshotLoaderProxy} */ (this._receiver);
+        loaderProxy.addConsumer(callback);
+    },
+
+    startSnapshotTransfer: function()
+    {
+        HeapProfilerAgent.getHeapSnapshot(this.uid);
+    },
+
+    snapshotConstructorName: function()
+    {
+        return "JSHeapSnapshot";
+    },
+
+    snapshotProxyConstructor: function()
+    {
+        return WebInspector.HeapSnapshotProxy;
+    },
+
+    _setupWorker: function()
+    {
+        function setProfileWait(event)
+        {
+            this.sidebarElement.wait = event.data;
+        }
+        var worker = new WebInspector.HeapSnapshotWorker();
+        worker.addEventListener("wait", setProfileWait, this);
+        var loaderProxy = worker.createLoader(this.snapshotConstructorName(), this.snapshotProxyConstructor());
+        loaderProxy.addConsumer(this._snapshotReceived.bind(this));
+        this._receiver = loaderProxy;
+    },
+
+    /**
+     * @override
+     */
+    dispose: function()
+    {
+        if (this._receiver)
+            this._receiver.close();
+        else if (this._snapshotProxy)
+            this._snapshotProxy.dispose();
+    },
+
+    /**
+     * @param {number} value
+     * @param {number} maxValue
+     */
+    _updateTransferProgress: function(value, maxValue)
+    {
+        var percentValue = ((maxValue ? (value / maxValue) : 0) * 100).toFixed(0);
+        if (this._savingToFile)
+            this.sidebarElement.subtitle = WebInspector.UIString("Saving\u2026 %d\%", percentValue);
+        else
+            this.sidebarElement.subtitle = WebInspector.UIString("Loading\u2026 %d\%", percentValue);
+    },
+
+    _updateSnapshotStatus: function()
+    {
+        this.sidebarElement.subtitle = Number.bytesToString(this._snapshotProxy.totalSize);
+        this.sidebarElement.wait = false;
+    },
+
+    /**
+     * @param {string} chunk
+     */
+    transferChunk: function(chunk)
+    {
+        ++this._numberOfChunks;
+        this._receiver.write(chunk, callback.bind(this));
+        function callback()
+        {
+            this._updateTransferProgress(++this._savedChunks, this._totalNumberOfChunks);
+            if (this._totalNumberOfChunks === this._savedChunks) {
+                if (this._savingToFile)
+                    this._updateSnapshotStatus();
+                else
+                    this.sidebarElement.subtitle = WebInspector.UIString("Parsing\u2026");
+
+                this._receiver.close();
+            }
+        }
+    },
+
+    _snapshotReceived: function(snapshotProxy)
+    {
+        this._receiver = null;
+        if (snapshotProxy)
+            this._snapshotProxy = snapshotProxy;
+        this._updateSnapshotStatus();
+        var worker = /** @type {WebInspector.HeapSnapshotWorker} */ (this._snapshotProxy.worker);
+        this.isTemporary = false;
+        worker.startCheckingForLongRunningCalls();
+    },
+
+    finishHeapSnapshot: function()
+    {
+        this._totalNumberOfChunks = this._numberOfChunks;
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    canSaveToFile: function()
+    {
+        return !this.fromFile() && !!this._snapshotProxy && !this._receiver;
+    },
+
+    /**
+     * @override
+     */
+    saveToFile: function()
+    {
+        this._numberOfChunks = 0;
+
+        var fileOutputStream = new WebInspector.FileOutputStream();
+        function onOpen()
+        {
+            this._receiver = fileOutputStream;
+            this._savedChunks = 0;
+            this._updateTransferProgress(0, this._totalNumberOfChunks);
+            HeapProfilerAgent.getHeapSnapshot(this.uid);
+        }
+        this._savingToFile = true;
+        this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
+        fileOutputStream.open(this._fileName, onOpen.bind(this));
+    },
+
+    /**
+     * @override
+     * @param {File} file
+     */
+    loadFromFile: function(file)
+    {
+        this.title = file.name;
+        this.sidebarElement.subtitle = WebInspector.UIString("Loading\u2026");
+        this.sidebarElement.wait = true;
+        this._setupWorker();
+        this._numberOfChunks = 0;
+        this._savingToFile = false;
+
+        var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this);
+        var fileReader = this._createFileReader(file, delegate);
+        fileReader.start(this._receiver);
+    },
+
+    _createFileReader: function(file, delegate)
+    {
+        return new WebInspector.ChunkedFileReader(file, 10000000, delegate);
+    },
+
+    __proto__: WebInspector.ProfileHeader.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.OutputStreamDelegate}
+ */
+WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
+{
+    this._snapshotHeader = snapshotHeader;
+}
+
+WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = {
+    onTransferStarted: function()
+    {
+    },
+
+    /**
+     * @param {WebInspector.ChunkedReader} reader
+     */
+    onChunkTransferred: function(reader)
+    {
+        this._snapshotHeader._updateTransferProgress(reader.loadedSize(), reader.fileSize());
+    },
+
+    onTransferFinished: function()
+    {
+        this._snapshotHeader.finishHeapSnapshot();
+    },
+
+    /**
+     * @param {WebInspector.ChunkedReader} reader
+     */
+    onError: function (reader, e)
+    {
+        switch(e.target.error.code) {
+        case e.target.error.NOT_FOUND_ERR:
+            this._snapshotHeader.sidebarElement.subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
+        break;
+        case e.target.error.NOT_READABLE_ERR:
+            this._snapshotHeader.sidebarElement.subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
+        break;
+        case e.target.error.ABORT_ERR:
+            break;
+        default:
+            this._snapshotHeader.sidebarElement.subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
+        }
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
+ */
+WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("flameChart.css");
+    this.element.id = "heap-recording-view";
+
+    this._overviewContainer = this.element.createChild("div", "overview-container");
+    this._overviewGrid = new WebInspector.OverviewGrid("heap-recording");
+    this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas");
+    this._overviewContainer.appendChild(this._overviewGrid.element);
+    this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator();
+    this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
+
+    this._profileSamples = heapProfileHeader._profileSamples;
+    var timestamps = this._profileSamples.timestamps;
+    var startTime = timestamps[0];
+    this._totalTime = timestamps[timestamps.length - 1] - startTime;
+    this._windowLeft = 0.0;
+    this._windowRight = 1.0;
+}
+
+WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
+
+WebInspector.HeapTrackingOverviewGrid.prototype = {
+    /**
+      * @param {number} width
+      * @param {number} height
+      */
+    _drawOverviewCanvas: function(width, height)
+    {
+        var sizes = this._profileSamples.sizes;
+        var usedSizes = this._profileSamples.max;
+        var timestamps = this._profileSamples.timestamps;
+
+        var scaleFactor = width / this._totalTime;
+        var maxUsedSize = 0;
+        var currentX = 0;
+        /**
+          * @param {Array.<number>} sizes
+          * @param {function(number, number):void} callback
+          */
+        function aggregateAndCall(sizes, callback)
+        {
+            var size = 0;
+            var currentX = 0;
+            for (var i = 1; i < timestamps.length; ++i) {
+                var x  = Math.floor((timestamps[i] - startTime) * scaleFactor) ;
+                if (x !== currentX) {
+                    if (size)
+                        callback(currentX, size);
+                    size = 0;
+                    currentX = x;
+                }
+                size += sizes[i];
+            }
+            callback(currentX, size);
+        }
+
+        /**
+          * @param {number} x
+          * @param {number} size
+          */
+        function maxUsedSizeCallback(x, size)
+        {
+            maxUsedSize = Math.max(maxUsedSize, size);
+        }
+
+        aggregateAndCall(usedSizes, maxUsedSizeCallback);
+
+        this._overviewCanvas.width = width * window.devicePixelRatio;
+        this._overviewCanvas.height = height * window.devicePixelRatio;
+        this._overviewCanvas.style.width = width + "px";
+        this._overviewCanvas.style.height = height + "px";
+        var yScaleFactor = height / (maxUsedSize * 1.1);
+        var startTime = timestamps[0];
+
+        var context = this._overviewCanvas.getContext("2d");
+        context.scale(window.devicePixelRatio, window.devicePixelRatio);
+
+        /**
+          * @param {number} x
+          * @param {number} size
+          */
+        function drawBarCallback(x, size)
+        {
+            context.moveTo(x, height - 1);
+            context.lineTo(x, Math.round(height - size * yScaleFactor - 1));
+        }
+
+        context.beginPath();
+        context.lineWidth = 2;
+        context.strokeStyle = "rgba(192, 192, 192, 0.6)";
+        aggregateAndCall(usedSizes, drawBarCallback);
+        context.stroke();
+        context.closePath();
+
+        context.beginPath();
+        context.lineWidth = 2;
+        context.strokeStyle = "rgba(0, 0, 192, 0.8)";
+        aggregateAndCall(sizes, drawBarCallback);
+        context.stroke();
+        context.closePath();
+    },
+
+    onResize: function()
+    {
+        this._updateOverviewCanvas = true;
+        this._scheduleUpdate();
+    },
+
+    _onWindowChanged: function()
+    {
+        if (!this._updateGridTimerId)
+            this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10);
+    },
+
+    _scheduleUpdate: function()
+    {
+        if (this._updateTimerId)
+            return;
+        this._updateTimerId = setTimeout(this.update.bind(this), 10);
+    },
+
+    _updateBoundaries: function()
+    {
+        this._windowLeft = this._overviewGrid.windowLeft();
+        this._windowRight = this._overviewGrid.windowRight();
+        this._windowWidth = this._windowRight - this._windowLeft;
+    },
+
+    /**
+     * @param {boolean} updateOverviewCanvas
+     */
+    update: function(updateOverviewCanvas)
+    {
+        this._updateTimerId = null;
+        this._updateBoundaries();
+        this._overviewCalculator._updateBoundaries(this);
+        this._overviewGrid.updateDividers(this._overviewCalculator);
+        if (this._updateOverviewCanvas || updateOverviewCanvas) {
+            this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
+            this._updateOverviewCanvas = false;
+        }
+    },
+
+    _updateGrid: function()
+    {
+        this._updateGridTimerId = 0;
+        this._updateBoundaries();
+        var ids = this._profileSamples.ids;
+        var timestamps = this._profileSamples.timestamps;
+        var sizes = this._profileSamples.sizes;
+        var startTime = timestamps[0];
+        var finishTime = timestamps[timestamps.length - 1];
+        var timeRange = finishTime - startTime;
+        var timeLeft = startTime + timeRange * this._windowLeft;
+        var timeRight = startTime + timeRange * this._windowRight;
+        var minId = 0;
+        var maxId = ids[ids.length - 1] + 1;
+        var size = 0;
+        for (var i = 1; i < timestamps.length; ++i) {
+            if (!timestamps[i])
+                continue;
+            if (timestamps[i] > timeRight)
+                break;
+            maxId = ids[i];
+            if (timestamps[i] <= timeLeft) {
+                minId = ids[i];
+                continue;
+            }
+            size += sizes[i];
+        }
+
+        this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelineGrid.Calculator}
+ */
+WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
+{
+}
+
+WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = {
+    /**
+     * @param {WebInspector.HeapTrackingOverviewGrid} chart
+     */
+    _updateBoundaries: function(chart)
+    {
+        this._minimumBoundaries = 0;
+        this._maximumBoundaries = chart._totalTime;
+        this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries;
+    },
+
+    /**
+     * @param {number} time
+     */
+    computePosition: function(time)
+    {
+        return (time - this._minimumBoundaries) * this._xScaleFactor;
+    },
+
+    formatTime: function(value)
+    {
+        return Number.secondsToString((value + this._minimumBoundaries) / 1000);
+    },
+
+    maximumBoundary: function()
+    {
+        return this._maximumBoundaries;
+    },
+
+    minimumBoundary: function()
+    {
+        return this._minimumBoundaries;
+    },
+
+    zeroTime: function()
+    {
+        return this._minimumBoundaries;
+    },
+
+    boundarySpan: function()
+    {
+        return this._maximumBoundaries - this._minimumBoundaries;
+    }
+}
diff --git a/Source/devtools/front_end/HeapSnapshotWorker.js b/Source/devtools/front_end/HeapSnapshotWorker.js
new file mode 100644
index 0000000..c29d266
--- /dev/null
+++ b/Source/devtools/front_end/HeapSnapshotWorker.js
@@ -0,0 +1,93 @@
+/*
+ * 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:
+ *
+ *     * 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 = {};
+WebInspector.UIString = function(s) { return s; };
+
+importScripts("HeapSnapshot.js");
+importScripts("HeapSnapshotLoader.js");
+importScripts("HeapSnapshotWorkerDispatcher.js");
+importScripts("JSHeapSnapshot.js");
+importScripts("NativeHeapSnapshot.js");
+importScripts("FileUtils.js");
+importScripts("utilities.js");
+
+function postMessageWrapper(message)
+{
+    postMessage(message);
+}
+
+/**
+ * @constructor
+ */
+WebInspector.WorkerConsole = function()
+{
+}
+
+WebInspector.WorkerConsole.prototype = {
+    /** @param {...*} var_args */
+    log: function(var_args)
+    {
+        this._postMessage("log", Array.prototype.slice.call(arguments));
+    },
+
+    /** @param {...*} var_args */
+    error: function(var_args)
+    {
+        this._postMessage("error", Array.prototype.slice.call(arguments));
+    },
+
+    /** @param {...*} var_args */
+    info: function(var_args)
+    {
+        this._postMessage("info", Array.prototype.slice.call(arguments));
+    },
+
+    trace: function()
+    {
+        this.log(new Error().stack);
+    },
+
+    /** @param {string} method */
+    /** @param {Array.<*>} args */
+    _postMessage: function(method, args)
+    {
+        var rawMessage = {
+            object: "console",
+            method: method,
+            arguments: args
+        };
+        postMessageWrapper(rawMessage);
+    }
+};
+
+var dispatcher = new WebInspector.HeapSnapshotWorkerDispatcher(this, postMessageWrapper);
+addEventListener("message", dispatcher.dispatchMessage.bind(dispatcher), false);
+console = new WebInspector.WorkerConsole();
diff --git a/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js b/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js
new file mode 100644
index 0000000..a13141a
--- /dev/null
+++ b/Source/devtools/front_end/HeapSnapshotWorkerDispatcher.js
@@ -0,0 +1,94 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.HeapSnapshotWorkerDispatcher = function(globalObject, postMessage)
+{
+    this._objects = [];
+    this._global = globalObject;
+    this._postMessage = postMessage;
+}
+
+WebInspector.HeapSnapshotWorkerDispatcher.prototype = {
+    _findFunction: function(name)
+    {
+        var path = name.split(".");
+        var result = this._global;
+        for (var i = 0; i < path.length; ++i)
+            result = result[path[i]];
+        return result;
+    },
+
+    dispatchMessage: function(event)
+    {
+        var data = event.data;
+        var response = {callId: data.callId};
+        try {
+            switch (data.disposition) {
+                case "create": {
+                    var constructorFunction = this._findFunction(data.methodName);
+                    this._objects[data.objectId] = new constructorFunction();
+                    break;
+                }
+                case "dispose": {
+                    delete this._objects[data.objectId];
+                    break;
+                }
+                case "getter": {
+                    var object = this._objects[data.objectId];
+                    var result = object[data.methodName];
+                    response.result = result;
+                    break;
+                }
+                case "factory": {
+                    var object = this._objects[data.objectId];
+                    var result = object[data.methodName].apply(object, data.methodArguments);
+                    if (result)
+                        this._objects[data.newObjectId] = result;
+                    response.result = !!result;
+                    break;
+                }
+                case "method": {
+                    var object = this._objects[data.objectId];
+                    response.result = object[data.methodName].apply(object, data.methodArguments);
+                    break;
+                }
+            }
+        } catch (e) {
+            response.error = e.toString();
+            response.errorCallStack = e.stack;
+            if (data.methodName)
+                response.errorMethodName = data.methodName;
+        }
+        this._postMessage(response);
+    }
+};
diff --git a/Source/devtools/front_end/HelpScreen.js b/Source/devtools/front_end/HelpScreen.js
new file mode 100644
index 0000000..20580b7
--- /dev/null
+++ b/Source/devtools/front_end/HelpScreen.js
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {string=} title
+ * @extends {WebInspector.View}
+ */
+WebInspector.HelpScreen = function(title)
+{
+    WebInspector.View.call(this);
+    this.markAsRoot();
+    this.registerRequiredCSS("helpScreen.css");
+
+    this.element.className = "help-window-outer";
+    this.element.addEventListener("keydown", this._onKeyDown.bind(this), false);
+    this.element.tabIndex = 0;
+    this.element.addEventListener("focus", this._onBlur.bind(this), false);
+
+    if (title) {
+        var mainWindow = this.element.createChild("div", "help-window-main");
+        var captionWindow = mainWindow.createChild("div", "help-window-caption");
+        captionWindow.appendChild(this._createCloseButton());
+        this.contentElement = mainWindow.createChild("div", "help-content");
+        captionWindow.createChild("h1", "help-window-title").textContent = title;
+    }
+}
+
+/**
+ * @type {WebInspector.HelpScreen}
+ */
+WebInspector.HelpScreen._visibleScreen = null;
+
+WebInspector.HelpScreen.prototype = {
+    _createCloseButton: function()
+    {
+        var closeButton = document.createElement("div");
+        closeButton.className = "help-close-button close-button-gray";
+        closeButton.addEventListener("click", this.hide.bind(this), false);
+        return closeButton;
+    },
+
+    showModal: function()
+    {
+        var visibleHelpScreen = WebInspector.HelpScreen._visibleScreen;
+        if (visibleHelpScreen === this)
+            return;
+
+        if (visibleHelpScreen)
+            visibleHelpScreen.hide();
+        WebInspector.HelpScreen._visibleScreen = this;
+        this.show(document.body);
+        this.focus();
+    },
+
+    hide: function()
+    {
+        if (!this.isShowing())
+            return;
+
+        WebInspector.HelpScreen._visibleScreen = null;
+
+        WebInspector.restoreFocusFromElement(this.element);
+        this.detach();
+    },
+
+    /**
+     * @param {number} keyCode
+     * @return {boolean}
+     */
+    isClosingKey: function(keyCode)
+    {
+        return [
+            WebInspector.KeyboardShortcut.Keys.Enter.code,
+            WebInspector.KeyboardShortcut.Keys.Esc.code,
+            WebInspector.KeyboardShortcut.Keys.Space.code,
+        ].indexOf(keyCode) >= 0;
+    },
+
+    _onKeyDown: function(event)
+    {
+        if (this.isShowing() && this.isClosingKey(event.keyCode)) {
+            this.hide();
+            event.consume();
+        }
+    },
+
+    _onBlur: function(event)
+    {
+        // Pretend we're modal, grab focus back if we're still shown.
+        if (this.isShowing() && !this.element.isSelfOrAncestor(event.target))
+            WebInspector.setCurrentFocusElement(this.element);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @param {string=} title
+ * @param {string=} message
+ * @extends {WebInspector.HelpScreen}
+ */
+WebInspector.HelpScreenUntilReload = function(title, message)
+{
+    WebInspector.HelpScreen.call(this, title);
+    var p = this.contentElement.createChild("p");
+    p.addStyleClass("help-section");
+    p.textContent = message;
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this.hide, this);
+}
+
+WebInspector.HelpScreenUntilReload.prototype = {
+    /**
+     * @override
+     */
+    willHide: function()
+    {
+        WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this.hide, this);
+        WebInspector.HelpScreen.prototype.willHide.call(this);
+    },
+
+    __proto__: WebInspector.HelpScreen.prototype
+}
+
diff --git a/Source/devtools/front_end/ImageView.js b/Source/devtools/front_end/ImageView.js
new file mode 100644
index 0000000..f5cb0dc
--- /dev/null
+++ b/Source/devtools/front_end/ImageView.js
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2007, 2008 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @extends {WebInspector.ResourceView}
+ * @constructor
+ */
+WebInspector.ImageView = function(resource)
+{
+    WebInspector.ResourceView.call(this, resource);
+
+    this.element.addStyleClass("image");
+}
+
+WebInspector.ImageView.prototype = {
+    hasContent: function()
+    {
+        return true;
+    },
+
+    wasShown: function()
+    {
+        this._createContentIfNeeded();
+    },
+
+    _createContentIfNeeded: function()
+    {
+        if (this._container)
+            return;
+
+        var imageContainer = document.createElement("div");
+        imageContainer.className = "image";
+        this.element.appendChild(imageContainer);
+
+        var imagePreviewElement = document.createElement("img");
+        imagePreviewElement.addStyleClass("resource-image-view");
+        imageContainer.appendChild(imagePreviewElement);
+        imagePreviewElement.addEventListener("contextmenu", this._contextMenu.bind(this), true);
+
+        this._container = document.createElement("div");
+        this._container.className = "info";
+        this.element.appendChild(this._container);
+
+        var imageNameElement = document.createElement("h1");
+        imageNameElement.className = "title";
+        imageNameElement.textContent = this.resource.displayName;
+        this._container.appendChild(imageNameElement);
+
+        var infoListElement = document.createElement("dl");
+        infoListElement.className = "infoList";
+
+        this.resource.populateImageSource(imagePreviewElement);
+
+        function onImageLoad()
+        {
+            var content = this.resource.content;
+            if (content)
+                var resourceSize = this._base64ToSize(content);
+            else
+                var resourceSize = this.resource.resourceSize;
+
+            var imageProperties = [
+                { name: WebInspector.UIString("Dimensions"), value: WebInspector.UIString("%d × %d", imagePreviewElement.naturalWidth, imagePreviewElement.naturalHeight) },
+                { name: WebInspector.UIString("File size"), value: Number.bytesToString(resourceSize) },
+                { name: WebInspector.UIString("MIME type"), value: this.resource.mimeType }
+            ];
+
+            infoListElement.removeChildren();
+            for (var i = 0; i < imageProperties.length; ++i) {
+                var dt = document.createElement("dt");
+                dt.textContent = imageProperties[i].name;
+                infoListElement.appendChild(dt);
+                var dd = document.createElement("dd");
+                dd.textContent = imageProperties[i].value;
+                infoListElement.appendChild(dd);
+            }
+            var dt = document.createElement("dt");
+            dt.textContent = WebInspector.UIString("URL");
+            infoListElement.appendChild(dt);
+            var dd = document.createElement("dd");
+            var externalResource = true;
+            dd.appendChild(WebInspector.linkifyURLAsNode(this.resource.url, undefined, undefined, externalResource));
+            infoListElement.appendChild(dd);
+
+            this._container.appendChild(infoListElement);
+        }
+        imagePreviewElement.addEventListener("load", onImageLoad.bind(this), false);
+    },
+
+    _base64ToSize: function(content)
+    {
+        if (!content.length)
+            return 0;
+        var size = (content.length || 0) * 3 / 4;
+        if (content.length > 0 && content[content.length - 1] === "=")
+            size--;
+        if (content.length > 1 && content[content.length - 2] === "=")
+            size--;
+        return size;
+    },
+
+    _contextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy image URL" : "Copy Image URL"), this._copyImageURL.bind(this));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open image in new tab" : "Open Image in New Tab"), this._openInNewTab.bind(this));
+        contextMenu.show();
+    },
+
+    _copyImageURL: function()
+    {
+        InspectorFrontendHost.copyText(this.resource.url);
+    },
+
+    _openInNewTab: function()
+    {
+        InspectorFrontendHost.openInNewTab(this.resource.url);
+    },
+
+    __proto__: WebInspector.ResourceView.prototype
+}
diff --git a/Source/devtools/front_end/Images/addIcon.png b/Source/devtools/front_end/Images/addIcon.png
new file mode 100644
index 0000000..f48f0ab
--- /dev/null
+++ b/Source/devtools/front_end/Images/addIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/applicationCache.png b/Source/devtools/front_end/Images/applicationCache.png
new file mode 100644
index 0000000..254fd5b
--- /dev/null
+++ b/Source/devtools/front_end/Images/applicationCache.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/back.png b/Source/devtools/front_end/Images/back.png
new file mode 100644
index 0000000..b1c0c19
--- /dev/null
+++ b/Source/devtools/front_end/Images/back.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/breakpoint2.png b/Source/devtools/front_end/Images/breakpoint2.png
new file mode 100644
index 0000000..6d87745
--- /dev/null
+++ b/Source/devtools/front_end/Images/breakpoint2.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/breakpoint2_2x.png b/Source/devtools/front_end/Images/breakpoint2_2x.png
new file mode 100644
index 0000000..f77370d
--- /dev/null
+++ b/Source/devtools/front_end/Images/breakpoint2_2x.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/breakpointBorder.png b/Source/devtools/front_end/Images/breakpointBorder.png
new file mode 100644
index 0000000..f15e02f
--- /dev/null
+++ b/Source/devtools/front_end/Images/breakpointBorder.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/breakpointConditional2.png b/Source/devtools/front_end/Images/breakpointConditional2.png
new file mode 100644
index 0000000..87bbc0e
--- /dev/null
+++ b/Source/devtools/front_end/Images/breakpointConditional2.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/breakpointConditional2_2x.png b/Source/devtools/front_end/Images/breakpointConditional2_2x.png
new file mode 100644
index 0000000..e2aa575
--- /dev/null
+++ b/Source/devtools/front_end/Images/breakpointConditional2_2x.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/breakpointConditionalBorder.png b/Source/devtools/front_end/Images/breakpointConditionalBorder.png
new file mode 100644
index 0000000..4bd5806
--- /dev/null
+++ b/Source/devtools/front_end/Images/breakpointConditionalBorder.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/breakpointConditionalCounterBorder.png b/Source/devtools/front_end/Images/breakpointConditionalCounterBorder.png
new file mode 100644
index 0000000..897b7a0
--- /dev/null
+++ b/Source/devtools/front_end/Images/breakpointConditionalCounterBorder.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/breakpointCounterBorder.png b/Source/devtools/front_end/Images/breakpointCounterBorder.png
new file mode 100644
index 0000000..0b3ea14
--- /dev/null
+++ b/Source/devtools/front_end/Images/breakpointCounterBorder.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/checker.png b/Source/devtools/front_end/Images/checker.png
new file mode 100644
index 0000000..816a4ec
--- /dev/null
+++ b/Source/devtools/front_end/Images/checker.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/cookie.png b/Source/devtools/front_end/Images/cookie.png
new file mode 100644
index 0000000..846db58
--- /dev/null
+++ b/Source/devtools/front_end/Images/cookie.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/database.png b/Source/devtools/front_end/Images/database.png
new file mode 100644
index 0000000..39a8506
--- /dev/null
+++ b/Source/devtools/front_end/Images/database.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/databaseTable.png b/Source/devtools/front_end/Images/databaseTable.png
new file mode 100644
index 0000000..5508174
--- /dev/null
+++ b/Source/devtools/front_end/Images/databaseTable.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/deleteIcon.png b/Source/devtools/front_end/Images/deleteIcon.png
new file mode 100644
index 0000000..a615d54
--- /dev/null
+++ b/Source/devtools/front_end/Images/deleteIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/domain.png b/Source/devtools/front_end/Images/domain.png
new file mode 100644
index 0000000..68c75f3
--- /dev/null
+++ b/Source/devtools/front_end/Images/domain.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/fileSystem.png b/Source/devtools/front_end/Images/fileSystem.png
new file mode 100644
index 0000000..f801ce6
--- /dev/null
+++ b/Source/devtools/front_end/Images/fileSystem.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/forward.png b/Source/devtools/front_end/Images/forward.png
new file mode 100644
index 0000000..843392b
--- /dev/null
+++ b/Source/devtools/front_end/Images/forward.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/frame.png b/Source/devtools/front_end/Images/frame.png
new file mode 100644
index 0000000..e17c829
--- /dev/null
+++ b/Source/devtools/front_end/Images/frame.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/glossyHeader.png b/Source/devtools/front_end/Images/glossyHeader.png
new file mode 100644
index 0000000..6b77999
--- /dev/null
+++ b/Source/devtools/front_end/Images/glossyHeader.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/glossyHeaderPressed.png b/Source/devtools/front_end/Images/glossyHeaderPressed.png
new file mode 100644
index 0000000..9a64b7c
--- /dev/null
+++ b/Source/devtools/front_end/Images/glossyHeaderPressed.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/glossyHeaderSelected.png b/Source/devtools/front_end/Images/glossyHeaderSelected.png
new file mode 100644
index 0000000..f7d615c
--- /dev/null
+++ b/Source/devtools/front_end/Images/glossyHeaderSelected.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/glossyHeaderSelectedPressed.png b/Source/devtools/front_end/Images/glossyHeaderSelectedPressed.png
new file mode 100644
index 0000000..75d37fb
--- /dev/null
+++ b/Source/devtools/front_end/Images/glossyHeaderSelectedPressed.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/graphLabelCalloutLeft.png b/Source/devtools/front_end/Images/graphLabelCalloutLeft.png
new file mode 100644
index 0000000..29fc042
--- /dev/null
+++ b/Source/devtools/front_end/Images/graphLabelCalloutLeft.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/graphLabelCalloutRight.png b/Source/devtools/front_end/Images/graphLabelCalloutRight.png
new file mode 100644
index 0000000..6c56a2b
--- /dev/null
+++ b/Source/devtools/front_end/Images/graphLabelCalloutRight.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/indexedDB.png b/Source/devtools/front_end/Images/indexedDB.png
new file mode 100644
index 0000000..d9b3c02
--- /dev/null
+++ b/Source/devtools/front_end/Images/indexedDB.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/indexedDBIndex.png b/Source/devtools/front_end/Images/indexedDBIndex.png
new file mode 100644
index 0000000..b58959c
--- /dev/null
+++ b/Source/devtools/front_end/Images/indexedDBIndex.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/indexedDBObjectStore.png b/Source/devtools/front_end/Images/indexedDBObjectStore.png
new file mode 100644
index 0000000..ce2396b
--- /dev/null
+++ b/Source/devtools/front_end/Images/indexedDBObjectStore.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/localStorage.png b/Source/devtools/front_end/Images/localStorage.png
new file mode 100644
index 0000000..003ac5d
--- /dev/null
+++ b/Source/devtools/front_end/Images/localStorage.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/namedFlowOverflow.png b/Source/devtools/front_end/Images/namedFlowOverflow.png
new file mode 100644
index 0000000..f966b1a
--- /dev/null
+++ b/Source/devtools/front_end/Images/namedFlowOverflow.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/paneAddButtons.png b/Source/devtools/front_end/Images/paneAddButtons.png
new file mode 100644
index 0000000..ff25b0f
--- /dev/null
+++ b/Source/devtools/front_end/Images/paneAddButtons.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/paneElementStateButtons.png b/Source/devtools/front_end/Images/paneElementStateButtons.png
new file mode 100644
index 0000000..af8dac0
--- /dev/null
+++ b/Source/devtools/front_end/Images/paneElementStateButtons.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/paneFilterButtons.png b/Source/devtools/front_end/Images/paneFilterButtons.png
new file mode 100644
index 0000000..6c52820
--- /dev/null
+++ b/Source/devtools/front_end/Images/paneFilterButtons.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/paneRefreshButtons.png b/Source/devtools/front_end/Images/paneRefreshButtons.png
new file mode 100644
index 0000000..92ff209
--- /dev/null
+++ b/Source/devtools/front_end/Images/paneRefreshButtons.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/paneSettingsButtons.png b/Source/devtools/front_end/Images/paneSettingsButtons.png
new file mode 100644
index 0000000..cb4f090
--- /dev/null
+++ b/Source/devtools/front_end/Images/paneSettingsButtons.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/popoverArrows.png b/Source/devtools/front_end/Images/popoverArrows.png
new file mode 100644
index 0000000..8b98caf
--- /dev/null
+++ b/Source/devtools/front_end/Images/popoverArrows.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/popoverBackground.png b/Source/devtools/front_end/Images/popoverBackground.png
new file mode 100644
index 0000000..e9ba86e
--- /dev/null
+++ b/Source/devtools/front_end/Images/popoverBackground.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/profileGroupIcon.png b/Source/devtools/front_end/Images/profileGroupIcon.png
new file mode 100644
index 0000000..ff78cb4
--- /dev/null
+++ b/Source/devtools/front_end/Images/profileGroupIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/profileIcon.png b/Source/devtools/front_end/Images/profileIcon.png
new file mode 100644
index 0000000..c0c4600
--- /dev/null
+++ b/Source/devtools/front_end/Images/profileIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/profileSmallIcon.png b/Source/devtools/front_end/Images/profileSmallIcon.png
new file mode 100644
index 0000000..e5c4ad5
--- /dev/null
+++ b/Source/devtools/front_end/Images/profileSmallIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/programCounterBorder.png b/Source/devtools/front_end/Images/programCounterBorder.png
new file mode 100644
index 0000000..10b0250
--- /dev/null
+++ b/Source/devtools/front_end/Images/programCounterBorder.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/radioDot.png b/Source/devtools/front_end/Images/radioDot.png
new file mode 100644
index 0000000..5d50890
--- /dev/null
+++ b/Source/devtools/front_end/Images/radioDot.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/regionEmpty.png b/Source/devtools/front_end/Images/regionEmpty.png
new file mode 100644
index 0000000..ed64c22
--- /dev/null
+++ b/Source/devtools/front_end/Images/regionEmpty.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/regionFit.png b/Source/devtools/front_end/Images/regionFit.png
new file mode 100644
index 0000000..90d4d50
--- /dev/null
+++ b/Source/devtools/front_end/Images/regionFit.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/regionOverset.png b/Source/devtools/front_end/Images/regionOverset.png
new file mode 100644
index 0000000..0738f1b
--- /dev/null
+++ b/Source/devtools/front_end/Images/regionOverset.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/resourceCSSIcon.png b/Source/devtools/front_end/Images/resourceCSSIcon.png
new file mode 100644
index 0000000..18828d0
--- /dev/null
+++ b/Source/devtools/front_end/Images/resourceCSSIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/resourceDocumentIcon.png b/Source/devtools/front_end/Images/resourceDocumentIcon.png
new file mode 100644
index 0000000..fdc10e4
--- /dev/null
+++ b/Source/devtools/front_end/Images/resourceDocumentIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/resourceDocumentIconSmall.png b/Source/devtools/front_end/Images/resourceDocumentIconSmall.png
new file mode 100644
index 0000000..64d9735
--- /dev/null
+++ b/Source/devtools/front_end/Images/resourceDocumentIconSmall.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/resourceJSIcon.png b/Source/devtools/front_end/Images/resourceJSIcon.png
new file mode 100644
index 0000000..c1b7218
--- /dev/null
+++ b/Source/devtools/front_end/Images/resourceJSIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/resourcePlainIcon.png b/Source/devtools/front_end/Images/resourcePlainIcon.png
new file mode 100644
index 0000000..8c82a4c
--- /dev/null
+++ b/Source/devtools/front_end/Images/resourcePlainIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/resourcePlainIconSmall.png b/Source/devtools/front_end/Images/resourcePlainIconSmall.png
new file mode 100644
index 0000000..5db0f5f
--- /dev/null
+++ b/Source/devtools/front_end/Images/resourcePlainIconSmall.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/resourcesTimeGraphIcon.png b/Source/devtools/front_end/Images/resourcesTimeGraphIcon.png
new file mode 100644
index 0000000..87de550
--- /dev/null
+++ b/Source/devtools/front_end/Images/resourcesTimeGraphIcon.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/searchNext.png b/Source/devtools/front_end/Images/searchNext.png
new file mode 100644
index 0000000..69e519e
--- /dev/null
+++ b/Source/devtools/front_end/Images/searchNext.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/searchPrev.png b/Source/devtools/front_end/Images/searchPrev.png
new file mode 100644
index 0000000..733d40b
--- /dev/null
+++ b/Source/devtools/front_end/Images/searchPrev.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/searchSmallBlue.png b/Source/devtools/front_end/Images/searchSmallBlue.png
new file mode 100644
index 0000000..08350f7
--- /dev/null
+++ b/Source/devtools/front_end/Images/searchSmallBlue.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/searchSmallBrightBlue.png b/Source/devtools/front_end/Images/searchSmallBrightBlue.png
new file mode 100644
index 0000000..09813af
--- /dev/null
+++ b/Source/devtools/front_end/Images/searchSmallBrightBlue.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/searchSmallGray.png b/Source/devtools/front_end/Images/searchSmallGray.png
new file mode 100644
index 0000000..7ffed62
--- /dev/null
+++ b/Source/devtools/front_end/Images/searchSmallGray.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/searchSmallWhite.png b/Source/devtools/front_end/Images/searchSmallWhite.png
new file mode 100644
index 0000000..a42bf1e
--- /dev/null
+++ b/Source/devtools/front_end/Images/searchSmallWhite.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/segment.png b/Source/devtools/front_end/Images/segment.png
new file mode 100644
index 0000000..348c18c
--- /dev/null
+++ b/Source/devtools/front_end/Images/segment.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/segmentEnd.png b/Source/devtools/front_end/Images/segmentEnd.png
new file mode 100644
index 0000000..89b155d
--- /dev/null
+++ b/Source/devtools/front_end/Images/segmentEnd.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/segmentHover.png b/Source/devtools/front_end/Images/segmentHover.png
new file mode 100644
index 0000000..b41a21c
--- /dev/null
+++ b/Source/devtools/front_end/Images/segmentHover.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/segmentHoverEnd.png b/Source/devtools/front_end/Images/segmentHoverEnd.png
new file mode 100644
index 0000000..4a7d679
--- /dev/null
+++ b/Source/devtools/front_end/Images/segmentHoverEnd.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/segmentSelected.png b/Source/devtools/front_end/Images/segmentSelected.png
new file mode 100644
index 0000000..8de6058
--- /dev/null
+++ b/Source/devtools/front_end/Images/segmentSelected.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/segmentSelectedEnd.png b/Source/devtools/front_end/Images/segmentSelectedEnd.png
new file mode 100644
index 0000000..2e01b4e
--- /dev/null
+++ b/Source/devtools/front_end/Images/segmentSelectedEnd.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/sessionStorage.png b/Source/devtools/front_end/Images/sessionStorage.png
new file mode 100644
index 0000000..732536f
--- /dev/null
+++ b/Source/devtools/front_end/Images/sessionStorage.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/spinner.gif b/Source/devtools/front_end/Images/spinner.gif
new file mode 100644
index 0000000..5f68c02
--- /dev/null
+++ b/Source/devtools/front_end/Images/spinner.gif
Binary files differ
diff --git a/Source/devtools/front_end/Images/spinnerActive.gif b/Source/devtools/front_end/Images/spinnerActive.gif
new file mode 100644
index 0000000..b75745c
--- /dev/null
+++ b/Source/devtools/front_end/Images/spinnerActive.gif
Binary files differ
diff --git a/Source/devtools/front_end/Images/spinnerActiveSelected.gif b/Source/devtools/front_end/Images/spinnerActiveSelected.gif
new file mode 100644
index 0000000..1ffb18b
--- /dev/null
+++ b/Source/devtools/front_end/Images/spinnerActiveSelected.gif
Binary files differ
diff --git a/Source/devtools/front_end/Images/spinnerInactive.gif b/Source/devtools/front_end/Images/spinnerInactive.gif
new file mode 100644
index 0000000..309cca0
--- /dev/null
+++ b/Source/devtools/front_end/Images/spinnerInactive.gif
Binary files differ
diff --git a/Source/devtools/front_end/Images/spinnerInactiveSelected.gif b/Source/devtools/front_end/Images/spinnerInactiveSelected.gif
new file mode 100644
index 0000000..40bc274
--- /dev/null
+++ b/Source/devtools/front_end/Images/spinnerInactiveSelected.gif
Binary files differ
diff --git a/Source/devtools/front_end/Images/src/breakpoints2.svg b/Source/devtools/front_end/Images/src/breakpoints2.svg
new file mode 100644
index 0000000..0ae7d3d
--- /dev/null
+++ b/Source/devtools/front_end/Images/src/breakpoints2.svg
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.3.1 r9886"
+   sodipodi:docname="breakpoints2.svg"
+   inkscape:export-filename="/usr/local/google/home/lushnikov/Desktop/bp.png"
+   inkscape:export-xdpi="44.838322"
+   inkscape:export-ydpi="44.838322">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient4408"
+       osb:paint="solid">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop4410" />
+    </linearGradient>
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lstart"
+       style="overflow:visible">
+      <path
+         id="path3786"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
+         transform="scale(0.8) translate(12.5,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.627417"
+     inkscape:cx="26.649581"
+     inkscape:cy="19.595518"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:snap-grids="true"
+     inkscape:snap-page="false"
+     inkscape:window-width="2495"
+     inkscape:window-height="1576"
+     inkscape:window-x="65"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2985"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:#698cfe;fill-opacity:1;fill-rule:nonzero;stroke:#4073f4;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       d="m 9.52174,1029.3622 38.04348,0 5.434781,10 -5.434781,10 -44.56522,0 0,-6 0,-8 c 0,-1.662 0,-4 0,-6 2.173914,0 4.715218,0 6.52174,0 z"
+       id="rect3816"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc"
+       inkscape:transform-center-x="-1.0000005" />
+    <path
+       style="fill:#ef9d0d;fill-opacity:1;fill-rule:nonzero;stroke:#a36c01;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       d="m 9.52174,993.36218 38.04348,0 5.43478,10.00002 -5.43478,10 -44.56522,0 0,-6 0,-8.00002 c 0,-1.662 0,-4 0,-6 2.173912,0 4.715218,0 6.52174,0 z"
+       id="rect3816-2"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc" />
+  </g>
+</svg>
diff --git a/Source/devtools/front_end/Images/src/statusbarButtonGlyphs.svg b/Source/devtools/front_end/Images/src/statusbarButtonGlyphs.svg
new file mode 100644
index 0000000..6e10e15
--- /dev/null
+++ b/Source/devtools/front_end/Images/src/statusbarButtonGlyphs.svg
@@ -0,0 +1,1735 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   height="120"
+   id="svg2"
+   version="1.1"
+   width="320"
+   xml:space="preserve"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="statusbarButtonGlyphs.svg"
+   inkscape:export-filename="/Users/pfeldman/code/chromium/src/third_party/WebKit/Source/devtools/front_end/Images/statusbarButtonGlyphs2x.png"
+   inkscape:export-xdpi="180"
+   inkscape:export-ydpi="180"><metadata
+   id="metadata3317"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="2"
+   gridtolerance="2"
+   guidetolerance="2"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="1436"
+   inkscape:window-height="856"
+   id="namedview3315"
+   showgrid="true"
+   width="640px"
+   inkscape:zoom="16"
+   inkscape:cx="114.60154"
+   inkscape:cy="15.701835"
+   inkscape:window-x="109"
+   inkscape:window-y="93"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="svg2"><inkscape:grid
+     empspacing="2"
+     visible="true"
+     enabled="true"
+     snapvisiblegridlinesonly="true"
+     spacingx="32px"
+     spacingy="24px"
+     type="xygrid"
+     id="grid3327"
+     dotted="false" /></sodipodi:namedview>
+  <defs
+   id="defs6"><linearGradient
+   id="linearGradient5337"><stop
+     style="stop-color:#e59290;stop-opacity:1;"
+     offset="0"
+     id="stop5339" /><stop
+     style="stop-color:#e99890;stop-opacity:1;"
+     offset="1"
+     id="stop5341" /></linearGradient><linearGradient
+   id="linearGradient5329"><stop
+     id="stop5331"
+     offset="0"
+     style="stop-color:#c0544f;stop-opacity:1;" /><stop
+     id="stop5333"
+     offset="1"
+     style="stop-color:#d08481;stop-opacity:1;" /></linearGradient><linearGradient
+   id="linearGradient5667"><stop
+     style="stop-color:#ffa801;stop-opacity:0;"
+     offset="0"
+     id="stop5669" /><stop
+     style="stop-color:#f0fb3d;stop-opacity:1;"
+     offset="1"
+     id="stop5671" /></linearGradient><linearGradient
+   id="linearGradient5655"><stop
+     id="stop5657"
+     offset="0"
+     style="stop-color:#ffbd00;stop-opacity:0.64957267;" /><stop
+     id="stop5659"
+     offset="1"
+     style="stop-color:#ffffff;stop-opacity:0.90598291;" /></linearGradient><linearGradient
+   id="linearGradient5647"><stop
+     style="stop-color:#a16a00;stop-opacity:1;"
+     offset="0"
+     id="stop5649" /><stop
+     style="stop-color:#c68200;stop-opacity:1;"
+     offset="1"
+     id="stop5651" /></linearGradient><linearGradient
+   id="linearGradient5505"><stop
+     style="stop-color:#00d600;stop-opacity:0;"
+     offset="0"
+     id="stop5507" /><stop
+     style="stop-color:#d8fc7b;stop-opacity:0.81196582;"
+     offset="1"
+     id="stop5509" /></linearGradient><linearGradient
+   id="linearGradient5497"><stop
+     id="stop5499"
+     offset="0"
+     style="stop-color:#00ba00;stop-opacity:1;" /><stop
+     id="stop5501"
+     offset="1"
+     style="stop-color:#ffffff;stop-opacity:0.90598291;" /></linearGradient><linearGradient
+   id="linearGradient5489"><stop
+     style="stop-color:#00a104;stop-opacity:1;"
+     offset="0"
+     id="stop5491" /><stop
+     style="stop-color:#00c605;stop-opacity:1;"
+     offset="1"
+     id="stop5493" /></linearGradient><linearGradient
+   id="linearGradient5441"><stop
+     id="stop5443"
+     offset="0"
+     style="stop-color:#ff0000;stop-opacity:0;" /><stop
+     id="stop5445"
+     offset="1"
+     style="stop-color:#f0cb68;stop-opacity:0.70940173;" /></linearGradient><linearGradient
+   id="linearGradient5429"><stop
+     style="stop-color:#e60000;stop-opacity:0.64957267;"
+     offset="0"
+     id="stop5431" /><stop
+     style="stop-color:#ffffff;stop-opacity:0.90598291;"
+     offset="1"
+     id="stop5433" /></linearGradient><linearGradient
+   id="linearGradient5421"><stop
+     style="stop-color:#ff0000;stop-opacity:1;"
+     offset="0"
+     id="stop5423" /><stop
+     style="stop-color:#9f0000;stop-opacity:1;"
+     offset="1"
+     id="stop5425" /></linearGradient><linearGradient
+   id="linearGradient5409"><stop
+     id="stop5411"
+     offset="0"
+     style="stop-color:#a10000;stop-opacity:1;" /><stop
+     id="stop5413"
+     offset="1"
+     style="stop-color:#c60000;stop-opacity:1;" /></linearGradient><linearGradient
+   id="linearGradient5401"><stop
+     style="stop-color:#000000;stop-opacity:1;"
+     offset="0"
+     id="stop5403" /><stop
+     style="stop-color:#000000;stop-opacity:0;"
+     offset="1"
+     id="stop5405" /></linearGradient><linearGradient
+   id="linearGradient5093"><stop
+     style="stop-color:#000000;stop-opacity:1;"
+     offset="0"
+     id="stop5095" /><stop
+     style="stop-color:#5c5c5c;stop-opacity:1;"
+     offset="1"
+     id="stop5097" /></linearGradient><linearGradient
+   id="linearGradient5083"><stop
+     style="stop-color:#000000;stop-opacity:1;"
+     offset="0"
+     id="stop5085" /><stop
+     style="stop-color:#929292;stop-opacity:1;"
+     offset="1"
+     id="stop5087" /></linearGradient><linearGradient
+   id="linearGradient4925"><stop
+     style="stop-color:#ac1f10;stop-opacity:1;"
+     offset="0"
+     id="stop4927" /><stop
+     style="stop-color:#f48f84;stop-opacity:1;"
+     offset="1"
+     id="stop4929" /></linearGradient><linearGradient
+   id="linearGradient4827"><stop
+     id="stop4829"
+     offset="0"
+     style="stop-color:#d7687d;stop-opacity:1;" /><stop
+     id="stop4831"
+     offset="1"
+     style="stop-color:#b21402;stop-opacity:1;" /></linearGradient><linearGradient
+   id="linearGradient4643"><stop
+     style="stop-color:#d76f7d;stop-opacity:1;"
+     offset="0"
+     id="stop4645" /><stop
+     style="stop-color:#b21402;stop-opacity:1;"
+     offset="1"
+     id="stop4647" /></linearGradient>
+    <mask
+   id="mask16" />
+    <clipPath
+   id="clipPath26">
+      <path
+   d="M 0,560 960,560 960,0 0,0 0,560 z"
+   id="path28"
+   inkscape:connector-curvature="0" />
+    </clipPath>
+    <clipPath
+   id="clipPath34">
+      <path
+   d="m 160,296 16.125,0 0,-14 -16.125,0 0,14 z"
+   id="path36"
+   inkscape:connector-curvature="0" />
+    </clipPath>
+    <clipPath
+   id="clipPath50">
+      <path
+   d="m 358,303 22,0 0,-4 -22,0 0,4 z"
+   id="path52"
+   inkscape:connector-curvature="0" />
+    </clipPath>
+    <clipPath
+   id="clipPath64">
+      <path
+   d="m 484.867,399.559 22.705,0 0,-19.118 -22.705,0 0,19.118 z"
+   id="path66"
+   inkscape:connector-curvature="0" />
+    </clipPath>
+    <clipPath
+   id="clipPath96">
+      <path
+   d="M 0,560 960,560 960,0 0,0 0,560 z"
+   id="path98"
+   inkscape:connector-curvature="0" />
+    </clipPath>
+    <clipPath
+   id="clipPath200">
+      <path
+   d="m 662,341 c 0,-9.941 8.059,-18 18,-18 l 0,0 c 9.942,0 18,8.059 18,18 l 0,0 c 0,9.941 -8.058,18 -18,18 l 0,0 c -9.941,0 -18,-8.059 -18,-18"
+   id="path202"
+   inkscape:connector-curvature="0" />
+    </clipPath>
+    <radialGradient
+   cx="0"
+   cy="0"
+   fx="0"
+   fy="0"
+   gradientTransform="matrix(17.999599,0,0,-17.999599,680,340.99951)"
+   gradientUnits="userSpaceOnUse"
+   id="radialGradient208"
+   r="1"
+   spreadMethod="pad">
+      <stop
+   id="stop210"
+   offset="0"
+   style="stop-color:#000000;stop-opacity:1" />
+      <stop
+   id="stop212"
+   offset="1"
+   style="stop-color:#000000;stop-opacity:0" />
+    </radialGradient>
+    <clipPath
+   id="clipPath220">
+      <path
+   d="M 0,560 960,560 960,0 0,0 0,560 z"
+   id="path222"
+   inkscape:connector-curvature="0" />
+    </clipPath>
+  
+    
+    
+    
+    
+    
+    
+    
+    
+    
+  
+      
+    
+      
+    
+      
+    
+      
+    
+      
+    
+      
+    
+      
+    
+      
+    
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+      
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+      
+        
+        
+        
+      <radialGradient
+   inkscape:collect="always"
+   xlink:href="#radialGradient208"
+   id="radialGradient3862"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(17.999599,0,0,-17.999599,680,340.99951)"
+   spreadMethod="pad"
+   cx="0"
+   cy="0"
+   fx="0"
+   fy="0"
+   r="1" /><radialGradient
+   inkscape:collect="always"
+   xlink:href="#radialGradient208"
+   id="radialGradient6632"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(17.999599,0,0,-17.999599,680,340.99951)"
+   spreadMethod="pad"
+   cx="0"
+   cy="0"
+   fx="0"
+   fy="0"
+   r="1" /><radialGradient
+   inkscape:collect="always"
+   xlink:href="#radialGradient208"
+   id="radialGradient6811"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(17.999599,0,0,-17.999599,680,340.99951)"
+   spreadMethod="pad"
+   cx="0"
+   cy="0"
+   fx="0"
+   fy="0"
+   r="1" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4643"
+   id="linearGradient4671"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4643"
+   id="linearGradient4759"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4643"
+   id="linearGradient4769"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4643"
+   id="linearGradient4785"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4643"
+   id="linearGradient4807"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4827"
+   id="linearGradient4825"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4643"
+   id="linearGradient4833"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4827"
+   id="linearGradient4857"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" />
+          
+        <linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4827"
+   id="linearGradient4890"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" />
+          
+        <linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4643"
+   id="linearGradient4915"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(-1,0,0,-1,24,0)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4925"
+   id="linearGradient4931"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0"
+   gradientUnits="userSpaceOnUse" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4827"
+   id="linearGradient4965"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0,-0.58333333,-0.58333333,0,120,111)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5083"
+   id="linearGradient5089"
+   x1="113"
+   y1="104"
+   x2="127"
+   y2="104"
+   gradientUnits="userSpaceOnUse" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5093"
+   id="linearGradient5099"
+   x1="113"
+   y1="104"
+   x2="127"
+   y2="104"
+   gradientUnits="userSpaceOnUse" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4827"
+   id="linearGradient5103"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0,-0.58333333,-0.58333333,0,120,111)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient4827"
+   id="linearGradient5281"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0,-0.58333333,-0.58333333,0,120,111)"
+   x1="0"
+   y1="0"
+   x2="24"
+   y2="0" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5409"
+   id="linearGradient5737"
+   gradientUnits="userSpaceOnUse"
+   x1="227.875"
+   y1="103.15625"
+   x2="235.125"
+   y2="103.15625" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5489"
+   id="linearGradient5743"
+   gradientUnits="userSpaceOnUse"
+   x1="227.875"
+   y1="103.15625"
+   x2="235.125"
+   y2="103.15625" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5497"
+   id="linearGradient5753"
+   gradientUnits="userSpaceOnUse"
+   x1="227.875"
+   y1="103.15625"
+   x2="235.125"
+   y2="103.15625" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5505"
+   id="linearGradient5755"
+   gradientUnits="userSpaceOnUse"
+   x1="227.875"
+   y1="103.15625"
+   x2="235.125"
+   y2="103.15625" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5429"
+   id="linearGradient5767"
+   gradientUnits="userSpaceOnUse"
+   x1="227.875"
+   y1="103.15625"
+   x2="235.125"
+   y2="103.15625" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5441"
+   id="linearGradient5769"
+   gradientUnits="userSpaceOnUse"
+   x1="227.875"
+   y1="103.15625"
+   x2="235.125"
+   y2="103.15625" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5655"
+   id="linearGradient5835"
+   gradientUnits="userSpaceOnUse"
+   x1="227.875"
+   y1="103.15625"
+   x2="235.125"
+   y2="103.15625" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5667"
+   id="linearGradient5837"
+   gradientUnits="userSpaceOnUse"
+   x1="227.875"
+   y1="103.15625"
+   x2="235.125"
+   y2="103.15625" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5329"
+   id="linearGradient5327"
+   x1="113"
+   y1="104"
+   x2="127"
+   y2="104"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0,-1,1,0,16,224)" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5329"
+   id="linearGradient5335"
+   gradientUnits="userSpaceOnUse"
+   x1="113"
+   y1="104"
+   x2="127"
+   y2="104"
+   gradientTransform="matrix(0,-1,1,0,16,224)" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5337"
+   id="linearGradient5343"
+   x1="96.5"
+   y1="103"
+   x2="109.5"
+   y2="103"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0,1,-1,0,206,0)" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5329"
+   id="linearGradient5354"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0,-1,1,0,16,224)"
+   x1="113"
+   y1="104"
+   x2="127"
+   y2="104" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5329"
+   id="linearGradient5356"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0,-1,1,0,16,224)"
+   x1="113"
+   y1="104"
+   x2="127"
+   y2="104" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5337"
+   id="linearGradient5358"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0,1,-1,0,206,0)"
+   x1="96.5"
+   y1="103"
+   x2="109.5"
+   y2="103" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient5337"
+   id="linearGradient5365"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0,1,-1,0,206,0)"
+   x1="96.5"
+   y1="103"
+   x2="109.5"
+   y2="103" /></defs>
+  
+<g
+   inkscape:groupmode="layer"
+   id="layer1"
+   inkscape:label="grid"
+   style="display:inline"
+   transform="translate(0,-580)" /><g
+   id="g12"
+   transform="matrix(0.50028185,0,0,-0.5104134,-36.680797,258.16231)">
+      <g
+   transform="matrix(639.91985,0,0,143.98197,72.039148,269.00968)"
+   id="g14" />
+    </g><g
+   id="g30"
+   transform="matrix(0.50028185,0,0,-0.5104134,-36.013832,209.91231)">
+          <g
+   id="g32" />
+          <g
+   id="g38">
+            <g
+   style="opacity:0.5"
+   id="g40"
+   clip-path="url(#clipPath34)">
+              <g
+   transform="translate(160,296)"
+   id="g42">
+                <path
+   inkscape:connector-curvature="0"
+   style="fill:#424242;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path44"
+   d="m 0,0 4,-5 0,-9 8,0 0,9 4.125,5 L 0,0 z" />
+              </g>
+            </g>
+          </g>
+        </g><g
+   id="g60"
+   transform="matrix(0.50650085,0,0,-0.52309465,-41.085547,214.0068)">
+          <g
+   id="g62" />
+          <g
+   id="g68">
+            <g
+   style="opacity:0.39999402"
+   id="g70"
+   clip-path="url(#clipPath64)">
+              <g
+   transform="translate(491.5488,386.3623)"
+   id="g72">
+                <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path74"
+   d="M 0,0 -3.618,4.311 -6.682,1.739 -0.254,-5.921 2.58,-3.076 16.023,10.367 13.195,13.196 0,0 z" />
+              </g>
+            </g>
+          </g>
+        </g><path
+   d="M 57,12 53.58829,8 39,8 l 0,8 14.58829,0"
+   id="path78"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" /><path
+   d="M 25,36 21.362197,32 19.570257,32 14,40 21.362197,40 25,36 z"
+   id="path82"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" /><path
+   d="M 7,32 7,40 9.0527005,40 14,32 7,32 z"
+   id="path86"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" /><path
+   d="M 9.668154,44.54737 8.07369,43.22813 18.831348,27.45264 20.42631,28.77187 9.668154,44.54737 z"
+   id="path90"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" /><g
+   transform="matrix(0.5063291,0,0,-0.5063291,16,15.10127)"
+   id="g100">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path102"
+   d="m 0,0 c -3.452,0 -6.25,2.798 -6.25,6.25 0,3.452 2.798,6.25 6.25,6.25 L 0,16 c -5.454,0 -9.875,-4.421 -9.875,-9.875 0,-5.454 4.421,-9.875 9.875,-9.875 5.454,0 9.875,4.421 9.875,9.875 l -3.631,0 C 6.176,2.731 3.41,0 0,0" />
+        </g><g
+   transform="matrix(0.49315069,0,0,-0.5303633,16,5)"
+   id="g104">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path106"
+   d="M 0,0 9.125,-5.657 0,-11.313" />
+        </g><path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path108"
+   d="m 53,37 -10,0 0,-5 10,0 0,5 z m -12,5 14,0 0,-12 -14,0 0,12 z" /><g
+   transform="matrix(0.5,0,0,-0.5,10,57)"
+   id="g110">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path112"
+   d="m 0,0 -4,0 0,-14 0,-4 4,0 18,0 0,4 -18,0 0,14 z" />
+        </g><path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path114"
+   d="m 13,56 7,0 0,5 -7,0 0,-5 z m 7,-2 -7,0 -2,0 0,2 0,5 0,2 2,0 7,0 2,0 0,-2 0,-5 0,-2 -2,0 z" /><g
+   id="g5185"><path
+     d="m 86,33 -8,0 0,-2 8,0 0,2 z"
+     id="path116"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><g
+     id="g118"
+     transform="matrix(0.49494449,0,0,-0.50824235,71.5,31.43731)">
+          <path
+   d="M 0,0 8.485,-8.485 0,-16.971 l 2.829,-2.828 8.485,8.485 2.829,2.829 -2.829,2.828 L 2.829,2.828 0,0 z"
+   id="path120"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><path
+     d="m 86,37 -6,0 0,-2 6,0 0,2 z"
+     id="path122"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 86,41 -8,0 0,-2 8,0 0,2 z"
+     id="path124"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /></g><g
+   transform="matrix(0.5,0,0,-0.5,80.4375,16.9375)"
+   id="g126">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path128"
+   d="m 0,0 c -4.97,0 -9,4.03 -9,9 0,1.76 0.513,3.397 1.387,4.785 L 4.785,1.387 C 3.397,0.513 1.76,0 0,0 M 9,9 C 9,7.24 8.487,5.603 7.613,4.215 L -4.785,16.613 C -3.397,17.487 -1.76,18 0,18 4.97,18 9,13.97 9,9 M 0.125,21.875 c -7.18,0 -13,-5.82 -13,-13 0,-7.18 5.82,-13 13,-13 7.18,0 13,5.82 13,13 0,7.18 -5.82,13 -13,13" />
+        </g><path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path130"
+   d="m 117,65 -10,0 0,-10 10,0 0,10 z" /><g
+   transform="matrix(0.5,0,0,-0.5,116,37)"
+   id="g132">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path134"
+   d="m 0,0 0,-8 -16,0 0,6 -2,0 0,-6 0,-2 2,0 18,0 0,2 0,8 -2,0 z" />
+        </g><g
+   transform="matrix(0.50647405,0,0,-0.52309465,111.88426,35.90276)"
+   id="g136">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path138"
+   d="m 0,0 -3.618,4.311 -3.064,-2.572 6.428,-7.66 2.835,2.845 13.443,13.443 -2.828,2.829 L 0,0 z" />
+        </g><g
+   transform="matrix(0.47140904,0,0,-0.47140904,149,8.33314)"
+   id="g140">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path142"
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z" />
+        </g><g
+   transform="matrix(0.5,0,0,-0.50757125,46.5,65.23466)"
+   id="g144">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path146"
+   d="m 0,0 c 0.639,-0.265 1.688,-0.478 2.933,-0.478 1.183,0 2.288,0.192 3.067,0.519 l 0,7.744 8,9 0,0.379 -22,0 0,-0.43 8,-9 L 0,0 z m 2.933,-3.478 c -2.31,0 -4.336,0.565 -5.42,1.512 L -3,-1.518 l 0,8.111 -8,9 0,4.571 28,0 0,-4.622 -8,-9 L 9,-1.601 8.381,-2.05 C 7.132,-2.958 5.146,-3.478 2.933,-3.478" />
+        </g><g
+   transform="matrix(0.5263158,0,0,-0.5058169,86,60)"
+   id="g148">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path150"
+   d="m 0,0 -19,9.885 0,-19.77" />
+        </g><g
+   id="g4283"
+   transform="matrix(0.5,0,0,0.5,0,-1)"><g
+     transform="matrix(1.0000001,0,0,-1,-74.000026,414.99999)"
+     id="g46">
+          <g
+   id="g48" />
+          <g
+   id="g54">
+            <g
+   clip-path="url(#clipPath50)"
+   id="g56"
+   style="opacity:0.19999701">
+              <path
+   d="m 380,299 -22,0 0,4 22,0 0,-4 z"
+   id="path58"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+            </g>
+          </g>
+        </g><g
+     id="g152"
+     transform="matrix(1.0000001,0,0,-1,270,110)">
+          <path
+   d="m 0,0 0,2 -2,0 -2,0 0,-2 0,-24 0,-2 2,0 2,0 0,2 -2,0 0,24 2,0 z"
+   id="path154"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><path
+     d="M 283.99999,116 272,116 l 0,-4.00001 11.99999,0 0,4.00001 z"
+     id="path156"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="M 289.99999,122 278,122 l 0,-4.00001 11.99999,0 0,4.00001 z"
+     id="path158"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 300,127.99999 -12.00001,0 0,-4 12.00001,0 0,4 z"
+     id="path160"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 303.99999,133.99999 -11.99999,0 0,-4 11.99999,0 0,4 z"
+     id="path162"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /></g><g
+   id="g4275"
+   transform="scale(0.5,0.5)"><path
+     d="M 339.99999,134 336,134 l 0,-18 3.99999,0 0,18 z"
+     id="path164"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 346,134 -4,0 0,-22 4,0 0,22 z"
+     id="path166"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 352,134 -4.00001,0 0,-26 4.00001,0 0,26 z"
+     id="path168"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 358,134 -4,0 0,-8 4,0 0,8 z"
+     id="path170"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="M 363.99999,134 360,134 l 0,-18 3.99999,0 0,18 z"
+     id="path172"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 370,134 -4,0 0,-22 4,0 0,22 z"
+     id="path174"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /></g><g
+   id="g4266"
+   transform="scale(0.5,0.5)"><path
+     d="m 416,114 -20,0 0,-4 20,0 0,4 z"
+     id="path176"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 434,114 -10,0 0,-4 10,0 0,4 z"
+     id="path178"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 400,122 -4,0 0,-4.00001 4,0 0,4.00001 z"
+     id="path180"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 426,122 -20.00001,0 0,-4.00001 20.00001,0 0,4.00001 z"
+     id="path182"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 434,122 -4.00001,0 0,-4.00001 4.00001,0 0,4.00001 z"
+     id="path184"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 410,130 -14,0 0,-4 14,0 0,4 z"
+     id="path186"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 434,130 -16,0 0,-4 16,0 0,4 z"
+     id="path188"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /></g><path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path190"
+   d="m 274,64 -7,0 0,-8 7,0 0,8 z m -9,2 14,0 0,-12 -14,0 0,12 z" /><g
+   transform="matrix(0.5,0,0,-0.5,298,12)"
+   id="g192">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path194"
+   d="m 0,0 c 0,-6.627 5.372,-12 12,-12 6.628,0 12,5.373 12,12 C 24,6.627 18.628,12 12,12 5.372,12 0,6.627 0,0" />
+        </g><g
+   clip-path="url(#clipPath200)"
+   id="g198"
+   transform="matrix(0.5,0,0,-0.5,-36,206.5)"
+   style="fill-rule:nonzero">
+        <g
+   id="g204"
+   style="fill-rule:nonzero">
+          <g
+   id="g206"
+   style="fill-rule:nonzero">
+            <path
+   d="m 662,341 c 0,-9.941 8.059,-18 18,-18 l 0,0 c 9.942,0 18,8.059 18,18 l 0,0 c 0,9.941 -8.058,18 -18,18 l 0,0 c -9.941,0 -18,-8.059 -18,-18"
+   id="path214"
+   style="fill:url(#radialGradient6811);fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+          </g>
+        </g>
+      </g><g
+   transform="matrix(0.5,0,0,-0.5,298,36)"
+   id="g224">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path226"
+   d="m 0,0 c 0,-6.627 5.372,-12 12,-12 6.628,0 12,5.373 12,12 C 24,6.627 18.628,12 12,12 5.372,12 0,6.627 0,0" />
+        </g><g
+   transform="matrix(0.50695545,0,0,-0.50695545,239.06259,38.10433)"
+   id="g228">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path230"
+   d="m 0,0 c -3.313,0 -6,2.686 -6,6 0,3.314 2.687,6 6,6 C 3.313,12 6,9.314 6,6 6,2.686 3.313,0 0,0 M 15.657,-6.829 7.613,1.215 C 8.487,2.603 9,4.24 9,6 c 0,4.971 -4.029,9 -9,9 -4.971,0 -9,-4.029 -9,-9 0,-4.971 4.029,-9 9,-9 1.761,0 3.397,0.513 4.785,1.387 l 8.043,-8.044 2.829,2.828 z" />
+        </g><g
+   transform="matrix(0.48874711,0,0,-0.49652038,233.7329,63.76329)"
+   id="g232">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none"
+   id="path234"
+   d="M 0,0 10.75,6.5 21.625,2 31.75,10.125" />
+        </g><g
+   transform="matrix(0.50028185,0,0,-0.5,231.00056,67)"
+   id="g236">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path238"
+   d="m 0,0 0,26 -2,0 0,-28 1,0 1,0 40,0 0,2 -40,0 z" />
+        </g><g
+   id="g4258"
+   transform="scale(0.5,0.5)"><path
+     d="m 474,22 -8,0 0,-8 8,0 0,8 z"
+     id="path240"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 494,18 -18.00001,0 0,-4 18.00001,0 0,4 z"
+     id="path242"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 494,22 -18.00001,0 0,-2 18.00001,0 0,2 z"
+     id="path244"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 494,30 -18.00001,0 0,-4.000001 18.00001,0 L 494,30 z"
+     id="path246"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 474,34 -8,0 0,-8.000001 8,0 L 474,34 z"
+     id="path248"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     d="m 494,34 -18.00001,0 0,-2 18.00001,0 0,2 z"
+     id="path250"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /></g><g
+   transform="matrix(0.53333335,0,0,-0.53333335,176,38.13333)"
+   id="g252">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path254"
+   d="M 0,0 C -2.209,0 -4,1.791 -4,4 -4,6.209 -2.209,8 0,8 2.209,8 4,6.209 4,4 4,1.791 2.209,0 0,0 M 15,7 9.5,8 12.728,12.485 8.485,16.728 3.875,13.375 3,19 -3,19 -3.75,13.125 -8.485,16.728 -12.728,12.485 -9,7.625 -15,7 l 0,-6 6.25,-0.625 -3.978,-4.86 4.243,-4.243 4.735,3.978 0.75,-6.25 6,0 0.75,6 4.735,-3.728 4.243,4.243 L 9.375,0.125 15,1 15,7 z" />
+        </g><g
+   transform="matrix(0.49058085,0,0,-0.5028495,174.9687,18.62521)"
+   id="g256">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path258"
+   d="M 0,0 C 0,1.788 0.87,2.682 2.61,2.682 3.462,2.682 4.114,2.448 4.563,1.979 5.013,1.513 5.238,0.853 5.238,0 5.238,-0.84 5.009,-1.506 4.555,-1.998 4.099,-2.488 3.45,-2.734 2.61,-2.734 1.77,-2.734 1.124,-2.495 0.674,-2.015 0.225,-1.536 0,-0.863 0,0 m 0.728,5.665 0,1.135 c 0,1.303 0.237,2.386 0.71,3.25 0.474,0.865 1.303,1.758 2.486,2.681 1.409,1.113 2.317,1.977 2.726,2.593 0.408,0.615 0.611,1.349 0.611,2.202 0,0.993 -0.331,1.757 -0.994,2.289 -0.662,0.535 -1.615,0.8 -2.858,0.8 -1.125,0 -2.166,-0.16 -3.125,-0.48 -0.958,-0.32 -1.894,-0.704 -2.806,-1.154 l -1.491,3.125 c 2.403,1.338 4.978,2.007 7.724,2.007 2.32,0 4.16,-0.569 5.521,-1.705 1.362,-1.136 2.043,-2.704 2.043,-4.705 0,-0.888 -0.13,-1.678 -0.39,-2.37 C 10.624,14.64 10.229,13.979 9.703,13.353 9.177,12.725 8.268,11.909 6.978,10.902 5.876,10.038 5.14,9.322 4.767,8.754 4.394,8.186 4.207,7.422 4.207,6.464 l 0,-0.799 -3.479,0 z" />
+        </g><g
+   transform="matrix(0.4940336,0,0,-0.48455482,210.33624,38.32707)"
+   id="g260">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path262"
+   d="m 0,0 c 0,-1.637 0.163,-2.862 0.487,-3.674 0.324,-0.813 0.848,-1.22 1.574,-1.22 1.438,0 2.157,1.631 2.157,4.894 0,3.218 -0.719,4.828 -2.157,4.828 C 1.335,4.828 0.811,4.432 0.487,3.642 0.163,2.851 0,1.637 0,0 m 7.416,0 c 0,-2.526 -0.457,-4.421 -1.369,-5.685 -0.911,-1.263 -2.24,-1.895 -3.986,-1.895 -1.67,0 -2.966,0.651 -3.889,1.953 -0.923,1.301 -1.385,3.177 -1.385,5.627 0,5.02 1.758,7.53 5.274,7.53 1.691,0 3.006,-0.648 3.946,-1.945 C 6.946,4.29 7.416,2.427 7.416,0 M 2.521,16.839 -10.84,-7.25 l -3.198,0 13.364,24.089 3.195,0 z M -15.686,9.622 c 0,-1.637 0.16,-2.855 0.478,-3.658 0.319,-0.801 0.841,-1.202 1.566,-1.202 1.451,0 2.174,1.619 2.174,4.86 0,3.241 -0.723,4.861 -2.174,4.861 -0.725,0 -1.247,-0.4 -1.566,-1.203 -0.318,-0.801 -0.478,-2.021 -0.478,-3.658 m 7.416,0.033 c 0,-2.526 -0.454,-4.424 -1.36,-5.692 -0.906,-1.27 -2.243,-1.904 -4.012,-1.904 -1.67,0 -2.964,0.656 -3.881,1.969 -0.917,1.313 -1.375,3.189 -1.375,5.627 0,5.02 1.752,7.53 5.256,7.53 1.725,0 3.05,-0.651 3.979,-1.952 0.928,-1.302 1.393,-3.161 1.393,-5.578" />
+        </g><g
+   transform="matrix(0.59916115,0,0,-0.5,268.00899,35.993)"
+   id="g264">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path266"
+   d="m 0,0 c 0.7,-0.458 1.185,-0.989 1.452,-1.596 0.268,-0.607 0.4,-1.653 0.4,-3.136 l 0,-2.039 c 0,-0.937 0.077,-1.526 0.231,-1.768 0.153,-0.24 0.443,-0.404 0.871,-0.489 0.104,-0.027 0.256,-0.055 0.461,-0.082 1.051,-0.154 1.577,-0.659 1.577,-1.514 0,-0.418 -0.153,-0.753 -0.459,-1.008 -0.305,-0.255 -0.713,-0.382 -1.221,-0.382 -0.65,0 -1.277,0.106 -1.883,0.317 -0.607,0.212 -1.109,0.502 -1.507,0.871 -0.435,0.395 -0.743,0.907 -0.923,1.533 -0.182,0.626 -0.271,1.698 -0.271,3.218 l 0,1.644 c 0,1.667 -0.768,2.625 -2.306,2.875 -0.066,0.019 -0.111,0.028 -0.139,0.028 -0.444,0.074 -0.772,0.234 -0.986,0.477 -0.214,0.242 -0.319,0.593 -0.319,1.051 0,0.402 0.089,0.719 0.271,0.953 0.18,0.234 0.47,0.402 0.868,0.505 0.176,0.056 0.43,0.116 0.764,0.18 1.23,0.23 1.847,1.144 1.847,2.74 l 0,1.647 c 0,1.413 0.068,2.405 0.209,2.973 0.137,0.569 0.374,1.065 0.708,1.49 0.388,0.472 0.909,0.84 1.561,1.103 0.654,0.263 1.373,0.395 2.16,0.395 0.49,0 0.885,-0.128 1.182,-0.382 0.295,-0.255 0.444,-0.59 0.444,-1.007 0,-0.87 -0.563,-1.394 -1.688,-1.57 C 3.2,9.019 3.127,9.009 3.08,9 2.615,8.926 2.295,8.757 2.118,8.495 1.941,8.231 1.852,7.634 1.852,6.703 l 0,-2.035 C 1.852,3.279 1.714,2.26 1.437,1.611 1.161,0.963 0.682,0.426 0,0" />
+        </g><g
+   transform="matrix(0.59904155,0,0,-0.5,276.98263,35.986)"
+   id="g268">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path270"
+   d="m 0,0 c -0.674,0.425 -1.147,0.961 -1.425,1.608 -0.276,0.647 -0.414,1.664 -0.414,3.051 l 0,2.018 0,0.88 c 0,0.43 -0.112,0.757 -0.336,0.982 -0.222,0.224 -0.595,0.374 -1.115,0.446 -0.679,0.094 -1.13,0.253 -1.353,0.477 -0.224,0.225 -0.336,0.566 -0.336,1.023 0,0.468 0.142,0.832 0.424,1.094 0.283,0.262 0.683,0.393 1.202,0.393 0.685,0 1.325,-0.102 1.916,-0.306 0.593,-0.204 1.088,-0.49 1.487,-0.86 C 0.504,10.39 0.822,9.874 1.008,9.259 1.193,8.644 1.285,7.559 1.285,6.005 l 0,-1.635 C 1.285,2.777 1.921,1.856 3.188,1.609 3.494,1.563 3.729,1.517 3.896,1.472 4.285,1.368 4.572,1.196 4.759,0.956 4.943,0.715 5.037,0.396 5.037,0 5.037,-0.415 4.938,-0.748 4.744,-0.999 4.55,-1.248 4.259,-1.42 3.869,-1.515 3.721,-1.542 3.512,-1.579 3.244,-1.626 1.938,-1.875 1.285,-2.815 1.285,-4.445 l 0,-1.611 c 0,-1.463 -0.068,-2.48 -0.207,-3.048 -0.14,-0.57 -0.375,-1.054 -0.709,-1.452 -0.362,-0.435 -0.882,-0.789 -1.562,-1.063 -0.681,-0.273 -1.392,-0.409 -2.133,-0.409 -0.518,0 -0.923,0.131 -1.215,0.396 -0.292,0.263 -0.438,0.631 -0.438,1.104 0,0.435 0.11,0.763 0.329,0.986 0.218,0.222 0.671,0.389 1.36,0.5 0.566,0.083 0.95,0.236 1.15,0.458 0.201,0.223 0.301,0.579 0.301,1.069 l 0,0.737 0,2.055 c 0,1.481 0.133,2.525 0.401,3.13 C -1.171,-0.988 -0.691,-0.457 0,0" />
+        </g><path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path272"
+   d="m 275.23077,14.69231 -2.15384,0 0,-5.38462 2.15384,0 0,5.38462 z m -4.3077,0 -2.15384,0 0,-5.38462 2.15384,0 0,5.38462 z M 274.92277,5 269.07722,5 265,9.03792 265,14.88238 269.07722,19 274.92277,19 279,14.88238 279,9.03792 274.92277,5 z" /><g
+   transform="matrix(0.5000139,0,0,-0.5,112.03125,15.5)"
+   id="g274">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path276"
+   d="m 0,0 c -3.866,0 -7,3.134 -7,7 0,3.866 3.134,7 7,7 C 3.866,14 7,10.866 7,7 7,3.134 3.866,0 0,0 m -0.062,17 c -4.875,0 -9.251,-1.667 -18,-10 8.624,-8.333 13.125,-10 18,-10 4.874,0 9.25,1.667 17.999,10 C 9.313,15.333 4.812,17 -0.062,17" />
+        </g><g
+   transform="matrix(0.5,0,0,-0.5,110.5,12)"
+   id="g278">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path280"
+   d="M 0,0 C 0,-1.657 1.343,-3 3,-3 4.657,-3 6,-1.657 6,0 6,1.657 4.657,3 3,3 1.343,3 0,1.657 0,0" />
+        </g><g
+   transform="matrix(0.5,0,0,-0.5,139.5,33)"
+   id="g282">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path284"
+   d="M 0,0 18,0 15,-20 3,-20" />
+        </g><g
+   transform="matrix(0.5,0,0,-0.5,147.5,30)"
+   id="g286">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path288"
+   d="m 0,0 -4,0 0,2 -6,0 0,-2 -4,0 c -1.104,0 -2,-0.964 -2,-2 l 0,-2 2,0 14,0 2,0 0,2 C 2,-0.964 1.104,0 0,0" />
+        </g><g
+   transform="matrix(0.5,0,0,-0.5,317,69)"
+   id="g290">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path292"
+   d="M 0,0 0,10 -10,0 0,0 z" />
+        </g><g
+   transform="matrix(0.5,0,0,-0.5,212,13)"
+   id="g294">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path296"
+   d="m 0,0 0,-8 -16,0 0,6 -2,0 0,-6 0,-2 2.428,0 L 2,-10 2,-8 2,0 0,0 z" />
+        </g><g
+   id="g3120"
+   transform="matrix(0.5,0,0,0.5,2,0)"><g
+     transform="matrix(0.84210526,0,0,-1.0116338,42,168)"
+     id="g3114">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path3116"
+   d="m 0,0 -19,9.885 0,-19.77" />
+        </g><path
+     inkscape:connector-curvature="0"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     id="path3118"
+     d="m 22,178 -6,0 0,-20 6,0 0,20 z" /></g><g
+   id="g4303"
+   transform="matrix(0.5,0,0,0.5,1,-1)"><path
+     d="m 92,178 -6,0 0,-16 6,0 0,16 z"
+     id="path3127"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     inkscape:connector-curvature="0"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     id="path3129"
+     d="m 104,178 -6,0 0,-16 6,0 0,16 z" /></g><g
+   id="g4323"
+   transform="scale(0.5,0.5)"><g
+     transform="matrix(0.33333333,0,0,-0.33333333,157,178)"
+     id="g3310">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path3312"
+   d="m 0,0 c 0,-6.627 5.372,-12 12,-12 6.628,0 12,5.373 12,12 C 24,6.627 18.628,12 12,12 5.372,12 0,6.627 0,0" />
+        </g><g
+     id="g4318"><g
+       id="g3314"
+       transform="matrix(0,0.42105263,0.91047041,0,161,172)">
+          <path
+   d="m 0,0 -19,9.885 0,-19.77"
+   id="path3316"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><path
+       d="m 164,164 -6,0 0,-8 6,0 0,8 z"
+       id="path3318"
+       style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       inkscape:connector-curvature="0" /></g></g><g
+   id="g4363"
+   transform="scale(0.5,0.5)"><g
+     id="g4333"
+     transform="matrix(0.33333333,0,0,-0.33333333,221,178)">
+          <path
+   d="m 0,0 c 0,-6.627 5.372,-12 12,-12 6.628,0 12,5.373 12,12 C 24,6.627 18.628,12 12,12 5.372,12 0,6.627 0,0"
+   id="path4335"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><g
+     id="g4339"
+     transform="matrix(0,-0.42105263,0.91047041,0,225,156)">
+          <path
+   d="m 0,0 -19,9.885 0,-19.77"
+   id="path4341"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path4351"
+   d="m 0,0 -19,9.885 0,-19.77" /></g><path
+     d="m 228,164 -6,0 0,8 6,0 0,-8 z"
+     id="path4343"
+     style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /></g><g
+   id="g4371"
+   transform="matrix(0.5,0,0,0.5,-0.371325,0.98892)"><g
+     transform="matrix(0.33333333,0,0,-0.33333333,285.10972,172.09028)"
+     id="g3386">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path3388"
+   d="m -9.6000008e-8,3.5999999 c 0,-6.627 5.371999996000008,-12 12.000000096000007,-12 6.628,0 12,5.373 12,12 C 24,10.227 18.628,15.6 12,15.6 5.3719999,15.6 -9.6000008e-8,10.227 -9.6000008e-8,3.5999999" />
+        </g><path
+     sodipodi:nodetypes="cc"
+     inkscape:connector-curvature="0"
+     id="path3400"
+     d="m 275.24265,172.08579 c 5.10629,-16.86347 22.80573,-17.45673 27.87622,0"
+     style="fill:none;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><g
+     inkscape:transform-center-y="4.8667427"
+     inkscape:transform-center-x="31.812074"
+     transform="matrix(0.12650103,0.40160031,-0.67542793,0.21275463,304.09531,176.02216)"
+     id="g4357">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path4359"
+   d="m 0,0 -19,9.885 0,-19.77" />
+        <path
+   d="m 0,0 -19,9.885 0,-19.77"
+   id="path4361"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" /></g></g><g
+   id="g4389"
+   transform="matrix(0.36842106,0,0,-0.40465352,12,102)">
+          <path
+   d="m 0,0 -19,9.885 0,-19.77"
+   id="path4391"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><g
+   transform="matrix(0,0.36842106,0.40465352,0,24,106)"
+   id="g4393">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path4395"
+   d="m 0,0 -19,9.885 0,-19.77" />
+        </g><g
+   id="g4397"
+   transform="matrix(0,-0.36842106,0.40465352,0,8,111)">
+          <path
+   d="m 0,0 -19,9.885 0,-19.77"
+   id="path4399"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><path
+   d="m 54,105 0,7 -10.000005,0 0,-7 z m 1,8 0,-11 -12,0 0,11 z"
+   id="path4987"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0"
+   sodipodi:nodetypes="cccccccccc" /><rect
+   style="opacity:0.81893005;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="rect4991"
+   width="12"
+   height="3"
+   x="73"
+   y="110"
+   ry="0" /><g
+   id="g6865"
+   transform="matrix(1,0,0,1.0833067,0,-7.5809332)"><g
+     transform="matrix(0.9994799,0,0,0.99934297,0.09079898,0.05570112)"
+     id="g6857"><g
+       id="g6850"><g
+         transform="matrix(0.49680733,0,0,0.49594275,1.1210985,0.73973011)"
+         id="g6083"><rect
+           y="158.80269"
+           x="336.90268"
+           height="22.194668"
+           width="28.194668"
+           id="rect4212"
+           style="fill:none;stroke:#000000;stroke-width:1.93673468;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><g
+           transform="matrix(-0.52631579,0,0,-0.60698027,349.99999,169.90003)"
+           id="g4216">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path4218"
+   d="M -19.118894,9.0253678e-4 0.01315753,9.9733228 l 0,-19.9448405" />
+        </g></g><rect
+         y="78.996201"
+         x="171.99866"
+         height="12.00789"
+         width="1.0005203"
+         id="rect6836"
+         style="fill:#000000;fill-opacity:1;stroke:none" /></g></g></g><g
+   id="g6963"
+   transform="matrix(1,0,0,1.0833067,0,-7.5809087)"><g
+     transform="translate(32.000017,-2.9395427e-4)"
+     id="g6874"><g
+       transform="matrix(0.9994799,0,0,0.99934297,0.09079898,0.05570112)"
+       id="g6876"><g
+         id="g6878"><g
+           transform="matrix(0.49680733,0,0,0.49594275,1.1210985,0.73973011)"
+           id="g6880"><rect
+             y="158.80269"
+             x="336.90268"
+             height="22.194668"
+             width="28.194668"
+             id="rect6882"
+             style="fill:none;stroke:#000000;stroke-width:1.93673468;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><g
+             transform="matrix(-0.52631579,0,0,-0.60698027,349.99999,169.90003)"
+             id="g6884">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path6886"
+   d="M 0.01315753,9.0253678e-4 -19.118894,9.9733228 l 0,-19.9448405" />
+        </g></g><rect
+           y="78.996201"
+           x="171.99866"
+           height="12.00789"
+           width="1.0005203"
+           id="rect6888"
+           style="fill:#000000;fill-opacity:1;stroke:none" /></g></g></g></g><g
+   id="g6954"
+   transform="matrix(1,0,0,1.0833067,0,-7.5809087)"><g
+     id="g6890"
+     transform="translate(64.000017,-2.9395427e-4)"><g
+       id="g6892"
+       transform="matrix(0.9994799,0,0,0.99934297,0.09079898,0.05570112)"><g
+         id="g6894"><g
+           id="g6896"
+           transform="matrix(0.49680733,0,0,0.49594275,1.1210985,0.73973011)"><rect
+             style="fill:none;stroke:#000000;stroke-width:1.93673468;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+             id="rect6898"
+             width="28.194668"
+             height="22.194668"
+             x="336.90268"
+             y="158.80269" /><g
+             id="g6900"
+             transform="matrix(-0.52631579,0,0,-0.60698027,349.99999,169.90003)">
+          
+        </g></g><rect
+           style="fill:#000000;fill-opacity:1;stroke:none"
+           id="rect6904"
+           width="1.0005203"
+           height="12.00789"
+           x="171.99866"
+           y="78.996201" /></g></g></g></g><g
+   transform="matrix(-1,0,0,1.0833067,448.00002,-7.5812271)"
+   id="g6906"><g
+     transform="matrix(0.9994799,0,0,0.99934297,0.09079898,0.05570112)"
+     id="g6908"><g
+       id="g6910"><g
+         transform="matrix(0.49680733,0,0,0.49594275,1.1210985,0.73973011)"
+         id="g6912"><rect
+           y="158.80269"
+           x="336.90268"
+           height="22.194668"
+           width="28.194668"
+           id="rect6914"
+           style="fill:none;stroke:#000000;stroke-width:1.93673468;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><g
+           transform="matrix(-0.52631579,0,0,-0.60698027,349.99999,169.90003)"
+           id="g6916">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path6918"
+   d="M 0.01315753,9.0253678e-4 -19.118894,9.9733228 l 0,-19.9448405" />
+        </g></g><rect
+         y="78.996201"
+         x="171.99866"
+         height="12.00789"
+         width="1.0005203"
+         id="rect6920"
+         style="fill:#000000;fill-opacity:1;stroke:none" /></g></g></g><g
+   id="g6938"
+   transform="matrix(-1,0,0,1.0833067,479.00002,-7.5812271)"><g
+     id="g6940"
+     transform="matrix(0.9994799,0,0,0.99934297,0.09079898,0.05570112)"><g
+       id="g6942"><g
+         id="g6944"
+         transform="matrix(0.49680733,0,0,0.49594275,1.1210985,0.73973011)"><rect
+           style="fill:none;stroke:#000000;stroke-width:1.93673468;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+           id="rect6946"
+           width="28.194668"
+           height="22.194668"
+           x="336.90268"
+           y="158.80269" /><g
+           id="g6948"
+           transform="matrix(-0.52631579,0,0,-0.60698027,349.99999,169.90003)">
+          <path
+   d="M -19.118894,9.0253678e-4 0.01315753,9.9733228 l 0,-19.9448405"
+   id="path6950"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g></g><rect
+         style="fill:#000000;fill-opacity:1;stroke:none"
+         id="rect6952"
+         width="1.0005203"
+         height="12.00789"
+         x="171.99866"
+         y="78.996201" /></g></g></g><g
+   id="g5024"
+   transform="translate(-2,-1)"><g
+     id="g4933"><path
+       inkscape:connector-curvature="0"
+       style="fill:url(#linearGradient5103);fill-opacity:1;fill-rule:nonzero;stroke:none"
+       id="path4791"
+       d="m 120,97 c -3.86575,0 -7,3.13367 -7,7 0,3.86633 3.13425,7 7,7 3.86575,0 7,-3.13367 7,-7 0,-3.86633 -3.13425,-7 -7,-7" /></g><path
+     inkscape:connector-curvature="0"
+     style="fill:#f27d82;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     id="path4795"
+     d="m 113.5,104 c 0,3.58963 2.90983,6.5 6.5,6.5 3.59017,0 6.5,-2.91037 6.5,-6.5 0,-3.58963 -2.90983,-6.5 -6.5,-6.5 -3.59017,0 -6.5,2.91037 -6.5,6.5" /><g
+     style="fill:#000000;fill-opacity:0.36444451;stroke:none"
+     transform="matrix(0.32998633,0,0,-0.32998633,123.5,101.9332)"
+     id="g4797">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:0.36444451;fill-rule:nonzero;stroke:none"
+   id="path4799"
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z" />
+        </g><g
+     id="g4801"
+     transform="matrix(0.32998633,0,0,-0.32998633,123.5,101.4332)"
+     style="fill:#ffffff;fill-opacity:1;stroke:none">
+          <path
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z"
+   id="path4803"
+   style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g></g><g
+   id="g5105"
+   transform="translate(-0.99999998,-0.99999868)"><g
+     id="g4875"
+     transform="matrix(0.32998633,0,0,-0.32998633,139.5,101.9332)"
+     style="fill:#000000;fill-opacity:0.24444442;stroke:none">
+          <path
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z"
+   id="path4877"
+   style="fill:#000000;fill-opacity:0.24444442;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><g
+     style="fill:#676767;fill-opacity:1;stroke:none"
+     transform="matrix(0.32998633,0,0,-0.32998633,139.5,101.4332)"
+     id="g4879">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#676767;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path4881"
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z" />
+        </g></g><g
+   id="g4951"
+   transform="matrix(-1,0,0,-1,223,207)"
+   style="fill:url(#linearGradient5356);fill-opacity:1;fill-rule:nonzero"><path
+     inkscape:connector-curvature="0"
+     style="fill:url(#linearGradient5354);fill-opacity:1;fill-rule:nonzero;stroke:none"
+     id="path4953"
+     d="m 120,97 c -3.86575,0 -7,3.13367 -7,7 0,3.86633 3.13425,7 7,7 3.86575,0 7,-3.13367 7,-7 0,-3.86633 -3.13425,-7 -7,-7" /></g><path
+   inkscape:connector-curvature="0"
+   style="fill:url(#linearGradient5365);fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path4955"
+   d="m 103,96.5 c -3.58963,0 -6.5,2.90983 -6.5,6.5 0,3.59017 2.91037,6.5 6.5,6.5 3.58963,0 6.5,-2.90983 6.5,-6.5 0,-3.59017 -2.91037,-6.5 -6.5,-6.5" /><g
+   style="fill:#993c35;fill-opacity:1;stroke:none"
+   transform="matrix(0.32998633,0,0,-0.32998633,106.5,100.9332)"
+   id="g4957">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#993c35;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path4959"
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z" />
+        </g><g
+   id="g4961"
+   transform="matrix(0.32998633,0,0,-0.32998633,106.5,100.4332)"
+   style="fill:#ffffff;fill-opacity:1;stroke:none">
+          <path
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z"
+   id="path4963"
+   style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><g
+   id="g5129"
+   transform="translate(-2.5,-1.5)"><path
+     inkscape:connector-curvature="0"
+     style="fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     id="path4998"
+     d="m 145.5,104 c 0,3.58963 2.90983,6.5 6.5,6.5 3.59017,0 6.5,-2.91037 6.5,-6.5 0,-3.58963 -2.90983,-6.5 -6.5,-6.5 -3.59017,0 -6.5,2.91037 -6.5,6.5" /><g
+     style="fill:#000000;fill-opacity:0.37333332;stroke:none"
+     transform="matrix(0.32998633,0,0,-0.32998633,155.5,101.9332)"
+     id="g5000">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:0.37333332;fill-rule:nonzero;stroke:none"
+   id="path5002"
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z" />
+        </g><g
+     id="g5004"
+     transform="matrix(0.32998633,0,0,-0.32998633,155.5,101.4332)"
+     style="fill:#ffffff;fill-opacity:1;stroke:none">
+          <path
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z"
+   id="path5006"
+   style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g></g><g
+   id="g5136"
+   transform="translate(-1.5,-1.5)"><path
+     d="m 161.5,104 c 0,3.58963 2.90983,6.5 6.5,6.5 3.59017,0 6.5,-2.91037 6.5,-6.5 0,-3.58963 -2.90983,-6.5 -6.5,-6.5 -3.59017,0 -6.5,2.91037 -6.5,6.5"
+     id="path5039"
+     style="fill:#9f9f9f;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><g
+     id="g5041"
+     transform="matrix(0.32998633,0,0,-0.32998633,171.5,101.9332)"
+     style="fill:#000000;fill-opacity:0.36444475;stroke:none">
+          <path
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z"
+   id="path5043"
+   style="fill:#000000;fill-opacity:0.36444475;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><g
+     style="fill:#ffffff;fill-opacity:1;stroke:none"
+     transform="matrix(0.32998633,0,0,-0.32998633,171.5,101.4332)"
+     id="g5045">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path5047"
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z" />
+        </g></g><g
+   transform="translate(45,-1.4999987)"
+   id="g5143"><g
+     style="fill:#000000;fill-opacity:0.24444442;stroke:none"
+     transform="matrix(0.32998633,0,0,-0.32998633,139.5,101.9332)"
+     id="g5145">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#000000;fill-opacity:0.24444442;fill-rule:nonzero;stroke:none"
+   id="path5147"
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z" />
+        </g><g
+     id="g5149"
+     transform="matrix(0.32998633,0,0,-0.32998633,139.5,101.4332)"
+     style="fill:#676767;fill-opacity:1;stroke:none">
+          <path
+   d="M 0,0 -2.828,2.828 -10.606,-4.95 -18.385,2.828 -21.213,0 l 7.778,-7.778 -7.778,-7.779 2.828,-2.828 7.779,7.779 7.778,-7.779 L 0,-15.557 -7.778,-7.778 0,0 z"
+   id="path5151"
+   style="fill:#676767;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g></g><g
+   id="g5165"
+   transform="matrix(0.396371,0,0,-0.47078517,131.64855,116.21248)">
+          <path
+   d="m 0,0 -3.618,4.311 -3.064,-2.572 6.428,-7.66 2.835,2.845 13.443,13.443 -2.828,2.829 L 0,0 z"
+   id="path5167"
+   style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   inkscape:connector-curvature="0" />
+        </g><path
+   style="fill:none;stroke:#367cf1;stroke-width:1.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+   d="m 195.75,97.750005 3.5,3.249985 -3.5,3.25001"
+   id="path5212"
+   inkscape:connector-curvature="0"
+   sodipodi:nodetypes="ccc" /><path
+   sodipodi:nodetypes="ccc"
+   inkscape:connector-curvature="0"
+   id="path5214"
+   d="m 195.75,108.75001 3.5,3.24998 -3.5,3.25001"
+   style="fill:none;stroke:#939393;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><g
+   id="g5222"
+   transform="translate(-2,0)"><g
+     style="fill:#bababa;fill-opacity:1"
+     transform="matrix(0.08333333,0,0,-0.08333333,211,101)"
+     id="g5202">
+          <path
+   inkscape:connector-curvature="0"
+   style="fill:#bababa;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path5204"
+   d="m 0,0 c 0,-6.627 5.372,-12 12,-12 6.628,0 12,5.373 12,12 C 24,6.627 18.628,12 12,12 5.372,12 0,6.627 0,0" />
+        </g><path
+     sodipodi:nodetypes="ccc"
+     inkscape:connector-curvature="0"
+     id="path5216"
+     d="m 210.25,97.750006 -3.5,3.249984 3.5,3.25001"
+     style="fill:none;stroke:#bababa;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g
+   id="g5272"
+   transform="translate(11.000005,-9.9999998)"><g
+     transform="matrix(0.71428571,0,0,0.71428571,121.28571,36.714286)"
+     id="g5241"><path
+       d="m 120,97 c -3.86575,0 -7,3.13367 -7,7 0,3.86633 3.13425,7 7,7 3.86575,0 7,-3.13367 7,-7 0,-3.86633 -3.13425,-7 -7,-7"
+       id="path5243"
+       style="fill:url(#linearGradient5281);fill-opacity:1;fill-rule:nonzero;stroke:none"
+       inkscape:connector-curvature="0" /></g><path
+     d="m 202.35714,111 c 0,2.56402 2.07845,4.64286 4.64286,4.64286 2.5644,0 4.64285,-2.07884 4.64285,-4.64286 0,-2.56402 -2.07845,-4.64286 -4.64285,-4.64286 -2.56441,0 -4.64286,2.07884 -4.64286,4.64286"
+     id="path5245"
+     style="fill:#eb3941;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     inkscape:connector-curvature="0" /><path
+     inkscape:connector-curvature="0"
+     id="path5264"
+     d="m 205,109 c 4,4 4,4 4,4"
+     style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
+     style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="m 209,109 c -4,4 -4,4 -4,4"
+     id="path5266"
+     inkscape:connector-curvature="0" /><path
+     style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="m 205,109 c 4,4 4,4 4,4"
+     id="path5268"
+     inkscape:connector-curvature="0" /><path
+     inkscape:connector-curvature="0"
+     id="path5270"
+     d="m 209,109 c -4,4 -4,4 -4,4"
+     style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /></g><path
+   sodipodi:nodetypes="cccc"
+   inkscape:connector-curvature="0"
+   id="path5289"
+   d="m 203,116 4,-7.77778 4,7.77778 z"
+   style="fill:none;stroke:#c19600;stroke-width:2;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
+   style="fill:#f4bd00;fill-opacity:1;stroke:#f5be00;stroke-width:1.49999988;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+   d="m 203.01662,116 4,-7.77778 4,7.77778 z"
+   id="path5283"
+   inkscape:connector-curvature="0"
+   sodipodi:nodetypes="cccc" /><text
+   sodipodi:linespacing="125%"
+   id="text5301"
+   y="146.8199"
+   x="161.33199"
+   style="font-size:11.26096152999999944px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ad8601;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Times Bold"
+   xml:space="preserve"
+   transform="scale(1.2629669,0.79178638)"><tspan
+     y="146.8199"
+     x="161.33199"
+     id="tspan5303"
+     sodipodi:role="line">!</tspan></text>
+
+<path
+   style="font-size:9.67697048px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Times Bold"
+   sodipodi:nodetypes="cccccccccccc"
+   inkscape:connector-curvature="0"
+   id="path5364"
+   d="m 206,110 2,0 0,2.30275 -0.28334,1.67983 -1.43333,0 L 206,112.30275 206,110 m 0,4.56933 2,0 0,1.43067 -2,0 0,-1" /><text
+   xml:space="preserve"
+   style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+   x="217.42622"
+   y="110.74906"
+   id="text5307"
+   sodipodi:linespacing="125%"><tspan
+     sodipodi:role="line"
+     id="tspan5309"
+     x="217.42622"
+     y="110.74906" /></text>
+
+<g
+   id="g5771"><path
+     sodipodi:type="arc"
+     style="fill:url(#linearGradient5737);fill-opacity:1;fill-rule:nonzero;stroke:none"
+     id="path5449"
+     sodipodi:cx="231.5"
+     sodipodi:cy="103.15625"
+     sodipodi:rx="3.625"
+     sodipodi:ry="3.84375"
+     d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+     transform="matrix(4.4216629e-4,1.3793103,-1.3008129,4.1702167e-4,363.08462,-218.35335)" /><path
+     transform="matrix(1.2413792,-3.9796805e-4,3.7530216e-4,1.1707316,-58.418,-19.676152)"
+     d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+     sodipodi:ry="3.84375"
+     sodipodi:rx="3.625"
+     sodipodi:cy="103.15625"
+     sodipodi:cx="231.5"
+     id="path5451"
+     style="fill:#dd0000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     sodipodi:type="arc" /><path
+     sodipodi:type="arc"
+     style="fill:url(#linearGradient5767);fill-opacity:1;fill-rule:nonzero;stroke:none"
+     id="path5453"
+     sodipodi:cx="231.5"
+     sodipodi:cy="103.15625"
+     sodipodi:rx="3.625"
+     sodipodi:ry="3.84375"
+     d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+     transform="matrix(-2.0173838e-4,-0.62931035,0.92682926,-2.9712795e-4,133.46895,244.52849)" /><path
+     transform="matrix(1.4094051e-4,0.43965518,0.78048781,-2.5021302e-4,148.48744,2.1206306)"
+     d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+     sodipodi:ry="3.84375"
+     sodipodi:rx="3.625"
+     sodipodi:cy="103.15625"
+     sodipodi:cx="231.5"
+     id="path5455"
+     style="fill:url(#linearGradient5769);fill-opacity:1;fill-rule:nonzero;stroke:none"
+     sodipodi:type="arc" /></g><g
+   id="g5757"><path
+     sodipodi:type="arc"
+     style="fill:url(#linearGradient5743);fill-opacity:1;fill-rule:nonzero;stroke:none"
+     id="path5513"
+     sodipodi:cx="231.5"
+     sodipodi:cy="103.15625"
+     sodipodi:rx="3.625"
+     sodipodi:ry="3.84375"
+     d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+     transform="matrix(-0.01506784,1.3792098,-1.3007182,-0.01421029,377.66542,-216.8212)" /><path
+     transform="matrix(1.2413037,0.01356118,-0.01278943,1.1706604,-46.042497,-22.900348)"
+     d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+     sodipodi:ry="3.84375"
+     sodipodi:rx="3.625"
+     sodipodi:cy="103.15625"
+     sodipodi:cx="231.5"
+     id="path5515"
+     style="fill:#00be00;fill-opacity:1;fill-rule:nonzero;stroke:none"
+     sodipodi:type="arc" /><path
+     sodipodi:type="arc"
+     style="fill:url(#linearGradient5753);fill-opacity:1;fill-rule:nonzero;stroke:none"
+     id="path5517"
+     sodipodi:cx="231.5"
+     sodipodi:cy="103.15625"
+     sodipodi:rx="3.625"
+     sodipodi:ry="3.84375"
+     d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+     transform="matrix(0.00687469,-0.62926454,0.92676181,0.01012483,142.86511,243.44332)" /><path
+     transform="matrix(-0.00480288,0.43962318,0.780431,0.00852617,160.60265,1.1603243)"
+     d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+     sodipodi:ry="3.84375"
+     sodipodi:rx="3.625"
+     sodipodi:cy="103.15625"
+     sodipodi:cx="231.5"
+     id="path5519"
+     style="fill:url(#linearGradient5755);fill-opacity:1;fill-rule:nonzero;stroke:none"
+     sodipodi:type="arc" /></g><path
+   sodipodi:type="arc"
+   style="fill:#e5a600;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path5588"
+   sodipodi:cx="231.5"
+   sodipodi:cy="103.15625"
+   sodipodi:rx="3.625"
+   sodipodi:ry="3.84375"
+   d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+   transform="matrix(4.4216629e-4,1.3793103,-1.3008129,4.1702167e-4,385.08462,-218.35335)" /><path
+   transform="matrix(1.2413792,-3.9796805e-4,3.7530216e-4,1.1707316,-36.418,-19.676152)"
+   d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+   sodipodi:ry="3.84375"
+   sodipodi:rx="3.625"
+   sodipodi:cy="103.15625"
+   sodipodi:cx="231.5"
+   id="path5590"
+   style="fill:#ffbd00;fill-opacity:1;fill-rule:nonzero;stroke:none"
+   sodipodi:type="arc" /><path
+   sodipodi:type="arc"
+   style="fill:url(#linearGradient5835);fill-opacity:1;fill-rule:nonzero;stroke:none"
+   id="path5592"
+   sodipodi:cx="231.5"
+   sodipodi:cy="103.15625"
+   sodipodi:rx="3.625"
+   sodipodi:ry="3.84375"
+   d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+   transform="matrix(-2.0173838e-4,-0.62931035,0.92682926,-2.9712795e-4,155.46895,244.52849)" /><path
+   transform="matrix(1.4094051e-4,0.43965518,0.78048781,-2.5021302e-4,170.44325,2.0764364)"
+   d="m 235.125,103.15625 a 3.625,3.84375 0 1 1 -7.25,0 3.625,3.84375 0 1 1 7.25,0 z"
+   sodipodi:ry="3.84375"
+   sodipodi:rx="3.625"
+   sodipodi:cy="103.15625"
+   sodipodi:cx="231.5"
+   id="path5594"
+   style="fill:url(#linearGradient5837);fill-opacity:1;fill-rule:nonzero;stroke:none"
+   sodipodi:type="arc" /></svg>
\ No newline at end of file
diff --git a/Source/devtools/front_end/Images/statusbarButtonGlyphs.png b/Source/devtools/front_end/Images/statusbarButtonGlyphs.png
new file mode 100644
index 0000000..49f0152
--- /dev/null
+++ b/Source/devtools/front_end/Images/statusbarButtonGlyphs.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/statusbarButtonGlyphs2x.png b/Source/devtools/front_end/Images/statusbarButtonGlyphs2x.png
new file mode 100644
index 0000000..dc20f2b
--- /dev/null
+++ b/Source/devtools/front_end/Images/statusbarButtonGlyphs2x.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/statusbarResizerHorizontal.png b/Source/devtools/front_end/Images/statusbarResizerHorizontal.png
new file mode 100644
index 0000000..674b895
--- /dev/null
+++ b/Source/devtools/front_end/Images/statusbarResizerHorizontal.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/statusbarResizerVertical.png b/Source/devtools/front_end/Images/statusbarResizerVertical.png
new file mode 100644
index 0000000..bf84d1e
--- /dev/null
+++ b/Source/devtools/front_end/Images/statusbarResizerVertical.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/thumbActiveHoriz.png b/Source/devtools/front_end/Images/thumbActiveHoriz.png
new file mode 100644
index 0000000..492c53f
--- /dev/null
+++ b/Source/devtools/front_end/Images/thumbActiveHoriz.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/thumbActiveVert.png b/Source/devtools/front_end/Images/thumbActiveVert.png
new file mode 100644
index 0000000..96d0745
--- /dev/null
+++ b/Source/devtools/front_end/Images/thumbActiveVert.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/thumbHoriz.png b/Source/devtools/front_end/Images/thumbHoriz.png
new file mode 100644
index 0000000..caed321
--- /dev/null
+++ b/Source/devtools/front_end/Images/thumbHoriz.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/thumbHoverHoriz.png b/Source/devtools/front_end/Images/thumbHoverHoriz.png
new file mode 100644
index 0000000..6942e23
--- /dev/null
+++ b/Source/devtools/front_end/Images/thumbHoverHoriz.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/thumbHoverVert.png b/Source/devtools/front_end/Images/thumbHoverVert.png
new file mode 100644
index 0000000..125bf79
--- /dev/null
+++ b/Source/devtools/front_end/Images/thumbHoverVert.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/thumbVert.png b/Source/devtools/front_end/Images/thumbVert.png
new file mode 100644
index 0000000..b90e94a
--- /dev/null
+++ b/Source/devtools/front_end/Images/thumbVert.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelineHollowPillBlue.png b/Source/devtools/front_end/Images/timelineHollowPillBlue.png
new file mode 100644
index 0000000..ed68e45
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelineHollowPillBlue.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelineHollowPillGray.png b/Source/devtools/front_end/Images/timelineHollowPillGray.png
new file mode 100644
index 0000000..12a6662
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelineHollowPillGray.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelineHollowPillGreen.png b/Source/devtools/front_end/Images/timelineHollowPillGreen.png
new file mode 100644
index 0000000..7b31b9e
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelineHollowPillGreen.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelineHollowPillOrange.png b/Source/devtools/front_end/Images/timelineHollowPillOrange.png
new file mode 100644
index 0000000..b76928e
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelineHollowPillOrange.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelineHollowPillPurple.png b/Source/devtools/front_end/Images/timelineHollowPillPurple.png
new file mode 100644
index 0000000..ce56400
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelineHollowPillPurple.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelineHollowPillRed.png b/Source/devtools/front_end/Images/timelineHollowPillRed.png
new file mode 100644
index 0000000..5cc45b8
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelineHollowPillRed.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelineHollowPillYellow.png b/Source/devtools/front_end/Images/timelineHollowPillYellow.png
new file mode 100644
index 0000000..b62f774
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelineHollowPillYellow.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelinePillBlue.png b/Source/devtools/front_end/Images/timelinePillBlue.png
new file mode 100644
index 0000000..ad4821d
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelinePillBlue.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelinePillGray.png b/Source/devtools/front_end/Images/timelinePillGray.png
new file mode 100644
index 0000000..4b4d182
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelinePillGray.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelinePillGreen.png b/Source/devtools/front_end/Images/timelinePillGreen.png
new file mode 100644
index 0000000..e6a62a8
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelinePillGreen.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelinePillOrange.png b/Source/devtools/front_end/Images/timelinePillOrange.png
new file mode 100644
index 0000000..76c9f56
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelinePillOrange.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelinePillPurple.png b/Source/devtools/front_end/Images/timelinePillPurple.png
new file mode 100644
index 0000000..b82cbd8
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelinePillPurple.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelinePillRed.png b/Source/devtools/front_end/Images/timelinePillRed.png
new file mode 100644
index 0000000..d8359fd
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelinePillRed.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/timelinePillYellow.png b/Source/devtools/front_end/Images/timelinePillYellow.png
new file mode 100644
index 0000000..a5e0d8f
--- /dev/null
+++ b/Source/devtools/front_end/Images/timelinePillYellow.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/toolbarIcons.png b/Source/devtools/front_end/Images/toolbarIcons.png
new file mode 100644
index 0000000..5d1f632
--- /dev/null
+++ b/Source/devtools/front_end/Images/toolbarIcons.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/toolbarIconsSmall.png b/Source/devtools/front_end/Images/toolbarIconsSmall.png
new file mode 100644
index 0000000..ca9ac1b
--- /dev/null
+++ b/Source/devtools/front_end/Images/toolbarIconsSmall.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/toolbarItemSelected.png b/Source/devtools/front_end/Images/toolbarItemSelected.png
new file mode 100644
index 0000000..505daf2
--- /dev/null
+++ b/Source/devtools/front_end/Images/toolbarItemSelected.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/trackHoriz.png b/Source/devtools/front_end/Images/trackHoriz.png
new file mode 100644
index 0000000..d5356d3
--- /dev/null
+++ b/Source/devtools/front_end/Images/trackHoriz.png
Binary files differ
diff --git a/Source/devtools/front_end/Images/trackVert.png b/Source/devtools/front_end/Images/trackVert.png
new file mode 100644
index 0000000..e9eddfb
--- /dev/null
+++ b/Source/devtools/front_end/Images/trackVert.png
Binary files differ
diff --git a/Source/devtools/front_end/IndexedDBModel.js b/Source/devtools/front_end/IndexedDBModel.js
new file mode 100644
index 0000000..451580e
--- /dev/null
+++ b/Source/devtools/front_end/IndexedDBModel.js
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.IndexedDBModel = function()
+{
+    IndexedDBAgent.enable();
+
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginAdded, this._securityOriginAdded, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginRemoved, this._securityOriginRemoved, this);
+
+    this._databases = new Map();
+    this._databaseNamesBySecurityOrigin = {};
+    this._reset();
+}
+
+WebInspector.IndexedDBModel.KeyTypes = {
+    NumberType:  "number",
+    StringType:  "string",
+    DateType:    "date",
+    ArrayType:   "array"
+};
+
+WebInspector.IndexedDBModel.KeyPathTypes = {
+    NullType:    "null",
+    StringType:  "string",
+    ArrayType:   "array"
+};
+
+WebInspector.IndexedDBModel.keyFromIDBKey = function(idbKey)
+{
+    if (typeof(idbKey) === "undefined" || idbKey === null)
+        return null;
+
+    var key = {};
+    switch (typeof(idbKey)) {
+    case "number":
+        key.number = idbKey;
+        key.type = WebInspector.IndexedDBModel.KeyTypes.NumberType;
+        break;
+    case "string":
+        key.string = idbKey;
+        key.type = WebInspector.IndexedDBModel.KeyTypes.StringType;
+        break;
+    case "object":
+        if (idbKey instanceof Date) {
+            key.date = idbKey.getTime();
+            key.type = WebInspector.IndexedDBModel.KeyTypes.DateType;
+        } else if (idbKey instanceof Array) {
+            key.array = [];
+            for (var i = 0; i < idbKey.length; ++i)
+                key.array.push(WebInspector.IndexedDBModel.keyFromIDBKey(idbKey[i]));
+            key.type = WebInspector.IndexedDBModel.KeyTypes.ArrayType;
+        }
+        break;
+    default:
+        return null;
+    }
+    return key;
+}
+
+WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange = function(idbKeyRange)
+{
+    var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
+    if (typeof(idbKeyRange) === "undefined" || idbKeyRange === null)
+        return null;
+
+    var keyRange = {};
+    keyRange.lower = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.lower);
+    keyRange.upper = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.upper);
+    keyRange.lowerOpen = idbKeyRange.lowerOpen;
+    keyRange.upperOpen = idbKeyRange.upperOpen;
+    return keyRange;
+}
+
+/**
+ * @param {IndexedDBAgent.KeyPath} keyPath
+ */
+WebInspector.IndexedDBModel.idbKeyPathFromKeyPath = function(keyPath)
+{
+    var idbKeyPath;
+    switch (keyPath.type) {
+    case WebInspector.IndexedDBModel.KeyPathTypes.NullType:
+        idbKeyPath = null;
+        break;
+    case WebInspector.IndexedDBModel.KeyPathTypes.StringType:
+        idbKeyPath = keyPath.string;
+        break;
+    case WebInspector.IndexedDBModel.KeyPathTypes.ArrayType:
+        idbKeyPath = keyPath.array;
+        break;
+    }
+    return idbKeyPath;
+}
+
+WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath = function(idbKeyPath)
+{
+    if (typeof idbKeyPath === "string")
+        return "\"" + idbKeyPath + "\"";
+    if (idbKeyPath instanceof Array)
+        return "[\"" + idbKeyPath.join("\", \"") + "\"]";
+    return null;
+}
+
+WebInspector.IndexedDBModel.EventTypes = {
+    DatabaseAdded: "DatabaseAdded",
+    DatabaseRemoved: "DatabaseRemoved",
+    DatabaseLoaded: "DatabaseLoaded"
+}
+
+WebInspector.IndexedDBModel.prototype = {
+    _reset: function()
+    {
+        for (var securityOrigin in this._databaseNamesBySecurityOrigin)
+            this._removeOrigin(securityOrigin);
+        var securityOrigins = WebInspector.resourceTreeModel.securityOrigins();
+        for (var i = 0; i < securityOrigins.length; ++i)
+            this._addOrigin(securityOrigins[i]);
+    },
+
+    refreshDatabaseNames: function()
+    {
+        for (var securityOrigin in this._databaseNamesBySecurityOrigin)
+            this._loadDatabaseNames(securityOrigin);
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+     */
+    refreshDatabase: function(databaseId)
+    {
+        this._loadDatabase(databaseId);
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+     * @param {string} objectStoreName
+     * @param {function()} callback
+     */
+    clearObjectStore: function(databaseId, objectStoreName, callback)
+    {
+        IndexedDBAgent.clearObjectStore(databaseId.securityOrigin, databaseId.name, objectStoreName, callback);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _securityOriginAdded: function(event)
+    {
+        var securityOrigin = /** @type {string} */ (event.data);
+        this._addOrigin(securityOrigin);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _securityOriginRemoved: function(event)
+    {
+        var securityOrigin = /** @type {string} */ (event.data);
+        this._removeOrigin(securityOrigin);
+    },
+
+    /**
+     * @param {string} securityOrigin
+     */
+    _addOrigin: function(securityOrigin)
+    {
+        console.assert(!this._databaseNamesBySecurityOrigin[securityOrigin]);
+        this._databaseNamesBySecurityOrigin[securityOrigin] = [];
+        this._loadDatabaseNames(securityOrigin);
+    },
+
+    /**
+     * @param {string} securityOrigin
+     */
+    _removeOrigin: function(securityOrigin)
+    {
+        console.assert(this._databaseNamesBySecurityOrigin[securityOrigin]);
+        for (var i = 0; i < this._databaseNamesBySecurityOrigin[securityOrigin].length; ++i)
+            this._databaseRemoved(securityOrigin, this._databaseNamesBySecurityOrigin[securityOrigin][i]);
+        delete this._databaseNamesBySecurityOrigin[securityOrigin];
+    },
+
+    /**
+     * @param {string} securityOrigin
+     * @param {Array.<string>} databaseNames
+     */
+    _updateOriginDatabaseNames: function(securityOrigin, databaseNames)
+    {
+        var newDatabaseNames = {};
+        for (var i = 0; i < databaseNames.length; ++i)
+            newDatabaseNames[databaseNames[i]] = true;
+        var oldDatabaseNames = {};
+        for (var i = 0; i < this._databaseNamesBySecurityOrigin[securityOrigin].length; ++i)
+            oldDatabaseNames[this._databaseNamesBySecurityOrigin[securityOrigin][i]] = true;
+
+        this._databaseNamesBySecurityOrigin[securityOrigin] = databaseNames;
+
+        for (var databaseName in oldDatabaseNames) {
+            if (!newDatabaseNames[databaseName])
+                this._databaseRemoved(securityOrigin, databaseName);
+        }
+        for (var databaseName in newDatabaseNames) {
+            if (!oldDatabaseNames[databaseName])
+                this._databaseAdded(securityOrigin, databaseName);
+        }
+    },
+
+    /**
+     * @param {string} securityOrigin
+     * @param {string} databaseName
+     */
+    _databaseAdded: function(securityOrigin, databaseName)
+    {
+        var databaseId = new WebInspector.IndexedDBModel.DatabaseId(securityOrigin, databaseName);
+        this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseAdded, databaseId);
+    },
+
+    /**
+     * @param {string} securityOrigin
+     * @param {string} databaseName
+     */
+    _databaseRemoved: function(securityOrigin, databaseName)
+    {
+        var databaseId = new WebInspector.IndexedDBModel.DatabaseId(securityOrigin, databaseName);
+        this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseRemoved, databaseId);
+    },
+
+    /**
+     * @param {string} securityOrigin
+     */
+    _loadDatabaseNames: function(securityOrigin)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {Array.<string>} databaseNames
+         */
+        function callback(error, databaseNames)
+        {
+            if (error) {
+                console.error("IndexedDBAgent error: " + error);
+                return;
+            }
+
+            if (!this._databaseNamesBySecurityOrigin[securityOrigin])
+                return;
+            this._updateOriginDatabaseNames(securityOrigin, databaseNames);
+        }
+
+        IndexedDBAgent.requestDatabaseNames(securityOrigin, callback.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+     */
+    _loadDatabase: function(databaseId)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {IndexedDBAgent.DatabaseWithObjectStores} databaseWithObjectStores
+         */
+        function callback(error, databaseWithObjectStores)
+        {
+            if (error) {
+                console.error("IndexedDBAgent error: " + error);
+                return;
+            }
+
+            if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
+                return;
+            var databaseModel = new WebInspector.IndexedDBModel.Database(databaseId, databaseWithObjectStores.version, databaseWithObjectStores.intVersion);
+            this._databases.put(databaseId, databaseModel);
+            for (var i = 0; i < databaseWithObjectStores.objectStores.length; ++i) {
+                var objectStore = databaseWithObjectStores.objectStores[i];
+                var objectStoreIDBKeyPath = WebInspector.IndexedDBModel.idbKeyPathFromKeyPath(objectStore.keyPath);
+                var objectStoreModel = new WebInspector.IndexedDBModel.ObjectStore(objectStore.name, objectStoreIDBKeyPath, objectStore.autoIncrement);
+                for (var j = 0; j < objectStore.indexes.length; ++j) {
+                     var index = objectStore.indexes[j];
+                     var indexIDBKeyPath = WebInspector.IndexedDBModel.idbKeyPathFromKeyPath(index.keyPath);
+                     var indexModel = new WebInspector.IndexedDBModel.Index(index.name, indexIDBKeyPath, index.unique, index.multiEntry);
+                     objectStoreModel.indexes[indexModel.name] = indexModel;
+                }
+                databaseModel.objectStores[objectStoreModel.name] = objectStoreModel;
+            }
+
+            this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseLoaded, databaseModel);
+        }
+
+        IndexedDBAgent.requestDatabase(databaseId.securityOrigin, databaseId.name, callback.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+     * @param {string} objectStoreName
+     * @param {webkitIDBKeyRange} idbKeyRange
+     * @param {number} skipCount
+     * @param {number} pageSize
+     * @param {function(Array.<WebInspector.IndexedDBModel.Entry>, boolean)} callback
+     */
+    loadObjectStoreData: function(databaseId, objectStoreName, idbKeyRange, skipCount, pageSize, callback)
+    {
+        this._requestData(databaseId, databaseId.name, objectStoreName, "", idbKeyRange, skipCount, pageSize, callback);
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+     * @param {string} objectStoreName
+     * @param {string} indexName
+     * @param {webkitIDBKeyRange} idbKeyRange
+     * @param {number} skipCount
+     * @param {number} pageSize
+     * @param {function(Array.<WebInspector.IndexedDBModel.Entry>, boolean)} callback
+     */
+    loadIndexData: function(databaseId, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
+    {
+        this._requestData(databaseId, databaseId.name, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback);
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+     * @param {string} databaseName
+     * @param {string} objectStoreName
+     * @param {string} indexName
+     * @param {webkitIDBKeyRange} idbKeyRange
+     * @param {number} skipCount
+     * @param {number} pageSize
+     * @param {function(Array.<WebInspector.IndexedDBModel.Entry>, boolean)} callback
+     */
+    _requestData: function(databaseId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {Array.<IndexedDBAgent.DataEntry>} dataEntries
+         * @param {boolean} hasMore
+         */
+        function innerCallback(error, dataEntries, hasMore)
+        {
+            if (error) {
+                console.error("IndexedDBAgent error: " + error);
+                return;
+            }
+
+            if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
+                return;
+            var entries = [];
+            for (var i = 0; i < dataEntries.length; ++i) {
+                var key = WebInspector.RemoteObject.fromPayload(dataEntries[i].key);
+                var primaryKey = WebInspector.RemoteObject.fromPayload(dataEntries[i].primaryKey);
+                var value = WebInspector.RemoteObject.fromPayload(dataEntries[i].value);
+                entries.push(new WebInspector.IndexedDBModel.Entry(key, primaryKey, value));
+            }
+            callback(entries, hasMore);
+        }
+
+        var keyRange = WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange(idbKeyRange);
+        IndexedDBAgent.requestData(databaseId.securityOrigin, databaseName, objectStoreName, indexName, skipCount, pageSize, keyRange ? keyRange : undefined, innerCallback.bind(this));
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.RemoteObject} key
+ * @param {WebInspector.RemoteObject} primaryKey
+ * @param {WebInspector.RemoteObject} value
+ */
+WebInspector.IndexedDBModel.Entry = function(key, primaryKey, value)
+{
+    this.key = key;
+    this.primaryKey = primaryKey;
+    this.value = value;
+}
+
+/**
+ * @constructor
+ * @param {string} securityOrigin
+ * @param {string} name
+ */
+WebInspector.IndexedDBModel.DatabaseId = function(securityOrigin, name)
+{
+    this.securityOrigin = securityOrigin;
+    this.name = name;
+}
+
+WebInspector.IndexedDBModel.DatabaseId.prototype = {
+    /**
+     * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+     */
+    equals: function(databaseId)
+    {
+        return this.name === databaseId.name && this.securityOrigin === databaseId.securityOrigin;
+    },
+}
+/**
+ * @constructor
+ * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+ * @param {string} version
+ */
+WebInspector.IndexedDBModel.Database = function(databaseId, version, intVersion)
+{
+    this.databaseId = databaseId;
+    this.version = version;
+    this.intVersion = intVersion;
+    this.objectStores = {};
+}
+
+/**
+ * @constructor
+ * @param {string} name
+ * @param {*} keyPath
+ */
+WebInspector.IndexedDBModel.ObjectStore = function(name, keyPath, autoIncrement)
+{
+    this.name = name;
+    this.keyPath = keyPath;
+    this.autoIncrement = autoIncrement;
+    this.indexes = {};
+}
+
+WebInspector.IndexedDBModel.ObjectStore.prototype = {
+    /**
+     * @type {string}
+     */
+    get keyPathString()
+    {
+        return WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath(this.keyPath);
+    }
+}
+
+/**
+ * @constructor
+ * @param {string} name
+ * @param {*} keyPath
+ */
+WebInspector.IndexedDBModel.Index = function(name, keyPath, unique, multiEntry)
+{
+    this.name = name;
+    this.keyPath = keyPath;
+    this.unique = unique;
+    this.multiEntry = multiEntry;
+}
+
+WebInspector.IndexedDBModel.Index.prototype = {
+    /**
+     * @type {string}
+     */
+    get keyPathString()
+    {
+        return WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath(this.keyPath);
+    }
+}
diff --git a/Source/devtools/front_end/IndexedDBViews.js b/Source/devtools/front_end/IndexedDBViews.js
new file mode 100644
index 0000000..8066707
--- /dev/null
+++ b/Source/devtools/front_end/IndexedDBViews.js
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.IndexedDBModel.Database} database
+ */
+WebInspector.IDBDatabaseView = function(database)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("indexedDBViews.css");
+
+    this.element.addStyleClass("fill");
+    this.element.addStyleClass("indexed-db-database-view");
+
+    this._headersListElement = this.element.createChild("ol", "outline-disclosure");
+    this._headersTreeOutline = new TreeOutline(this._headersListElement);
+    this._headersTreeOutline.expandTreeElementsWhenArrowing = true;
+
+    this._securityOriginTreeElement = new TreeElement("", null, false);
+    this._securityOriginTreeElement.selectable = false;
+    this._headersTreeOutline.appendChild(this._securityOriginTreeElement);
+
+    this._nameTreeElement = new TreeElement("", null, false);
+    this._nameTreeElement.selectable = false;
+    this._headersTreeOutline.appendChild(this._nameTreeElement);
+
+    this._intVersionTreeElement = new TreeElement("", null, false);
+    this._intVersionTreeElement.selectable = false;
+    this._headersTreeOutline.appendChild(this._intVersionTreeElement);
+
+    this._stringVersionTreeElement = new TreeElement("", null, false);
+    this._stringVersionTreeElement.selectable = false;
+    this._headersTreeOutline.appendChild(this._stringVersionTreeElement);
+
+    this.update(database);
+}
+
+WebInspector.IDBDatabaseView.prototype = {
+    /**
+     * @param {string} name
+     * @param {string} value
+     */
+    _formatHeader: function(name, value)
+    {
+        var fragment = document.createDocumentFragment();
+        fragment.createChild("div", "attribute-name").textContent = name + ":";
+        fragment.createChild("div", "attribute-value source-code").textContent = value;
+
+        return fragment;
+    },
+
+    _refreshDatabase: function()
+    {
+        this._securityOriginTreeElement.title = this._formatHeader(WebInspector.UIString("Security origin"), this._database.databaseId.securityOrigin);
+        this._nameTreeElement.title = this._formatHeader(WebInspector.UIString("Name"), this._database.databaseId.name);
+        this._stringVersionTreeElement.title = this._formatHeader(WebInspector.UIString("String Version"), this._database.version);
+        this._intVersionTreeElement.title = this._formatHeader(WebInspector.UIString("Integer Version"), this._database.intVersion);
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.Database} database
+     */
+    update: function(database)
+    {
+        this._database = database;
+        this._refreshDatabase();
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.IndexedDBModel} model
+ * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+ * @param {WebInspector.IndexedDBModel.ObjectStore} objectStore
+ * @param {WebInspector.IndexedDBModel.Index} index
+ */
+WebInspector.IDBDataView = function(model, databaseId, objectStore, index)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("indexedDBViews.css");
+
+    this._model = model;
+    this._databaseId = databaseId;
+    this._isIndex = !!index;
+
+    this.element.addStyleClass("indexed-db-data-view");
+
+    var editorToolbar = this._createEditorToolbar();
+    this.element.appendChild(editorToolbar);
+
+    this._dataGridContainer = this.element.createChild("div", "fill");
+    this._dataGridContainer.addStyleClass("data-grid-container");
+
+    this._refreshButton = new WebInspector.StatusBarButton(WebInspector.UIString("Refresh"), "refresh-storage-status-bar-item");
+    this._refreshButton.addEventListener("click", this._refreshButtonClicked, this);
+
+    this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear object store"), "clear-storage-status-bar-item");
+    this._clearButton.addEventListener("click", this._clearButtonClicked, this);
+
+    this._pageSize = 50;
+    this._skipCount = 0;
+
+    this.update(objectStore, index);
+    this._entries = [];
+}
+
+WebInspector.IDBDataView.prototype = {
+    /**
+     * @return {WebInspector.DataGrid}
+     */
+    _createDataGrid: function()
+    {
+        var keyPath = this._isIndex ? this._index.keyPath : this._objectStore.keyPath;
+
+        var columns = [];
+        columns.push({id: "number", title: WebInspector.UIString("#"), width: "50px"});
+        columns.push({id: "key", titleDOMFragment: this._keyColumnHeaderFragment(WebInspector.UIString("Key"), keyPath)});
+        if (this._isIndex)
+            columns.push({id: "primaryKey", titleDOMFragment: this._keyColumnHeaderFragment(WebInspector.UIString("Primary key"), this._objectStore.keyPath)});
+        columns.push({id: "value", title: WebInspector.UIString("Value")});
+
+        var dataGrid = new WebInspector.DataGrid(columns);
+        return dataGrid;
+    },
+
+    /**
+     * @param {string} prefix
+     * @param {*} keyPath
+     * @return {DocumentFragment}
+     */
+    _keyColumnHeaderFragment: function(prefix, keyPath)
+    {
+        var keyColumnHeaderFragment = document.createDocumentFragment();
+        keyColumnHeaderFragment.appendChild(document.createTextNode(prefix));
+        if (keyPath === null)
+            return keyColumnHeaderFragment;
+
+        keyColumnHeaderFragment.appendChild(document.createTextNode(" (" + WebInspector.UIString("Key path: ")));
+        if (keyPath instanceof Array) {
+            keyColumnHeaderFragment.appendChild(document.createTextNode("["));
+            for (var i = 0; i < keyPath.length; ++i) {
+                if (i != 0)
+                    keyColumnHeaderFragment.appendChild(document.createTextNode(", "));
+                keyColumnHeaderFragment.appendChild(this._keyPathStringFragment(keyPath[i]));
+            }
+            keyColumnHeaderFragment.appendChild(document.createTextNode("]"));
+        } else {
+            var keyPathString = /** @type {string} */ (keyPath);
+            keyColumnHeaderFragment.appendChild(this._keyPathStringFragment(keyPathString));
+        }
+        keyColumnHeaderFragment.appendChild(document.createTextNode(")"));
+        return keyColumnHeaderFragment;
+    },
+
+    /**
+     * @param {string} keyPathString
+     * @return {DocumentFragment}
+     */
+    _keyPathStringFragment: function(keyPathString)
+    {
+        var keyPathStringFragment = document.createDocumentFragment();
+        keyPathStringFragment.appendChild(document.createTextNode("\""));
+        var keyPathSpan = keyPathStringFragment.createChild("span", "source-code console-formatted-string");
+        keyPathSpan.textContent = keyPathString;
+        keyPathStringFragment.appendChild(document.createTextNode("\""));
+        return keyPathStringFragment;
+    },
+
+    /**
+     * @return {Element}
+     */
+    _createEditorToolbar: function()
+    {
+        var editorToolbar = document.createElement("div");
+        editorToolbar.addStyleClass("status-bar");
+        editorToolbar.addStyleClass("data-view-toolbar");
+
+        this._pageBackButton = editorToolbar.createChild("button", "back-button");
+        this._pageBackButton.addStyleClass("status-bar-item");
+        this._pageBackButton.title = WebInspector.UIString("Show previous page.");
+        this._pageBackButton.disabled = true;
+        this._pageBackButton.appendChild(document.createElement("img"));
+        this._pageBackButton.addEventListener("click", this._pageBackButtonClicked.bind(this), false);
+        editorToolbar.appendChild(this._pageBackButton);
+
+        this._pageForwardButton = editorToolbar.createChild("button", "forward-button");
+        this._pageForwardButton.addStyleClass("status-bar-item");
+        this._pageForwardButton.title = WebInspector.UIString("Show next page.");
+        this._pageForwardButton.disabled = true;
+        this._pageForwardButton.appendChild(document.createElement("img"));
+        this._pageForwardButton.addEventListener("click", this._pageForwardButtonClicked.bind(this), false);
+        editorToolbar.appendChild(this._pageForwardButton);
+
+        this._keyInputElement = editorToolbar.createChild("input", "key-input");
+        this._keyInputElement.placeholder = WebInspector.UIString("Start from key");
+        this._keyInputElement.addEventListener("paste", this._keyInputChanged.bind(this));
+        this._keyInputElement.addEventListener("cut", this._keyInputChanged.bind(this));
+        this._keyInputElement.addEventListener("keypress", this._keyInputChanged.bind(this));
+        this._keyInputElement.addEventListener("keydown", this._keyInputChanged.bind(this));
+
+        return editorToolbar;
+    },
+
+    _pageBackButtonClicked: function()
+    {
+        this._skipCount = Math.max(0, this._skipCount - this._pageSize);
+        this._updateData(false);
+    },
+
+    _pageForwardButtonClicked: function()
+    {
+        this._skipCount = this._skipCount + this._pageSize;
+        this._updateData(false);
+    },
+
+    _keyInputChanged: function()
+    {
+        window.setTimeout(this._updateData.bind(this, false), 0);
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.ObjectStore} objectStore
+     * @param {WebInspector.IndexedDBModel.Index} index
+     */
+    update: function(objectStore, index)
+    {
+        this._objectStore = objectStore;
+        this._index = index;
+
+        if (this._dataGrid)
+            this._dataGrid.detach();
+        this._dataGrid = this._createDataGrid();
+        this._dataGrid.show(this._dataGridContainer);
+
+        this._skipCount = 0;
+        this._updateData(true);
+    },
+
+    /**
+     * @param {string} keyString
+     */
+    _parseKey: function(keyString)
+    {
+        var result;
+        try {
+            result = JSON.parse(keyString);
+        } catch (e) {
+            result = keyString;
+        }
+        return result;
+    },
+
+    /**
+     * @return {string}
+     */
+    _stringifyKey: function(key)
+    {
+        if (typeof(key) === "string")
+            return key;
+        return JSON.stringify(key);
+    },
+
+    /**
+     * @param {boolean} force
+     */
+    _updateData: function(force)
+    {
+        var key = this._parseKey(this._keyInputElement.value);
+        var pageSize = this._pageSize;
+        var skipCount = this._skipCount;
+        this._refreshButton.setEnabled(false);
+        this._clearButton.setEnabled(!this._isIndex);
+
+        if (!force && this._lastKey === key && this._lastPageSize === pageSize && this._lastSkipCount === skipCount)
+            return;
+
+        if (this._lastKey !== key || this._lastPageSize !== pageSize) {
+            skipCount = 0;
+            this._skipCount = 0;
+        }
+        this._lastKey = key;
+        this._lastPageSize = pageSize;
+        this._lastSkipCount = skipCount;
+
+        /**
+         * @param {Array.<WebInspector.IndexedDBModel.Entry>} entries
+         * @param {boolean} hasMore
+         */
+        function callback(entries, hasMore)
+        {
+            this._refreshButton.setEnabled(true);
+            this.clear();
+            this._entries = entries;
+            for (var i = 0; i < entries.length; ++i) {
+                var data = {};
+                data["number"] = i + skipCount;
+                data["key"] = entries[i].key;
+                data["primaryKey"] = entries[i].primaryKey;
+                data["value"] = entries[i].value;
+
+                var primaryKey = JSON.stringify(this._isIndex ? entries[i].primaryKey : entries[i].key);
+                var node = new WebInspector.IDBDataGridNode(data);
+                this._dataGrid.rootNode().appendChild(node);
+            }
+
+            this._pageBackButton.disabled = skipCount === 0;
+            this._pageForwardButton.disabled = !hasMore;
+        }
+
+        var idbKeyRange = key ? window.webkitIDBKeyRange.lowerBound(key) : null;
+        if (this._isIndex)
+            this._model.loadIndexData(this._databaseId, this._objectStore.name, this._index.name, idbKeyRange, skipCount, pageSize, callback.bind(this));
+        else
+            this._model.loadObjectStoreData(this._databaseId, this._objectStore.name, idbKeyRange, skipCount, pageSize, callback.bind(this));
+    },
+
+    _refreshButtonClicked: function(event)
+    {
+        this._updateData(true);
+    },
+
+    _clearButtonClicked: function(event)
+    {
+        function cleared() {
+            this._clearButton.setEnabled(true);
+            this._updateData(true);
+        }
+        this._clearButton.setEnabled(false);
+        this._model.clearObjectStore(this._databaseId, this._objectStore.name, cleared.bind(this));
+    },
+
+    get statusBarItems()
+    {
+        return [this._refreshButton.element, this._clearButton.element];
+    },
+
+    clear: function()
+    {
+        this._dataGrid.rootNode().removeChildren();
+        for (var i = 0; i < this._entries.length; ++i) {
+            this._entries[i].key.release();
+            this._entries[i].primaryKey.release();
+            this._entries[i].value.release();
+        }
+        this._entries = [];
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ * @param {*} data
+ */
+WebInspector.IDBDataGridNode = function(data)
+{
+    WebInspector.DataGridNode.call(this, data, false);
+    this.selectable = false;
+}
+
+WebInspector.IDBDataGridNode.prototype = {
+    /**
+     * @return {Element}
+     */
+    createCell: function(columnIdentifier)
+    {
+        var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
+        var value = this.data[columnIdentifier];
+
+        switch (columnIdentifier) {
+        case "value":
+        case "key":
+        case "primaryKey":
+            cell.removeChildren();
+            this._formatValue(cell, value);
+            break;
+        default:
+        }
+
+        return cell;
+    },
+
+    _formatValue: function(cell, value)
+    {
+        var type = value.subtype || value.type;
+        var contents = cell.createChild("div", "source-code console-formatted-" + type);
+
+        switch (type) {
+        case "object":
+        case "array":
+            var section = new WebInspector.ObjectPropertiesSection(value, value.description)
+            section.editable = false;
+            section.skipProto = true;
+            contents.appendChild(section.element);
+            break;
+        case "string":
+            contents.addStyleClass("primitive-value");
+            contents.appendChild(document.createTextNode("\"" + value.description + "\""));
+            break;
+        default:
+            contents.addStyleClass("primitive-value");
+            contents.appendChild(document.createTextNode(value.description));
+        }
+    },
+
+    __proto__: WebInspector.DataGridNode.prototype
+}
+
diff --git a/Source/devtools/front_end/InspectElementModeController.js b/Source/devtools/front_end/InspectElementModeController.js
new file mode 100644
index 0000000..9fbf9b8
--- /dev/null
+++ b/Source/devtools/front_end/InspectElementModeController.js
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.InspectElementModeController = function()
+{
+    this.toggleSearchButton = new WebInspector.StatusBarButton(WebInspector.UIString("Select an element in the page to inspect it."), "node-search-status-bar-item");
+    this.toggleSearchButton.addEventListener("click", this.toggleSearch, this);
+    this._shortcut = WebInspector.InspectElementModeController.createShortcut();
+}
+
+WebInspector.InspectElementModeController.createShortcut = function()
+{
+    return WebInspector.KeyboardShortcut.makeDescriptor("c", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta | WebInspector.KeyboardShortcut.Modifiers.Shift);
+}
+
+WebInspector.InspectElementModeController.prototype = {
+    enabled: function()
+    {
+        return this.toggleSearchButton.toggled;
+    },
+
+    disable: function()
+    {
+        if (this.enabled())
+            this.toggleSearch();
+    },
+
+    toggleSearch: function()
+    {
+        var enabled = !this.enabled();
+        /**
+         * @param {?Protocol.Error} error
+         */
+        function callback(error)
+        {
+            if (!error)
+                this.toggleSearchButton.toggled = enabled;
+        }
+        WebInspector.domAgent.setInspectModeEnabled(enabled, callback.bind(this));
+    },
+
+    /**
+     * @param {KeyboardEvent} event
+     * @return {boolean}
+     */
+    handleShortcut: function(event)
+    {
+        if (WebInspector.KeyboardShortcut.makeKeyFromEvent(event) !== this._shortcut.key)
+            return false;
+        this.toggleSearch();
+        event.consume(true);
+        return true;
+    }
+}
diff --git a/Source/devtools/front_end/InspectorBackend.js b/Source/devtools/front_end/InspectorBackend.js
new file mode 100644
index 0000000..632793a
--- /dev/null
+++ b/Source/devtools/front_end/InspectorBackend.js
@@ -0,0 +1,404 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+function InspectorBackendClass()
+{
+    this._lastCallbackId = 1;
+    this._pendingResponsesCount = 0;
+    this._callbacks = {};
+    this._domainDispatchers = {};
+    this._eventArgs = {};
+    this._replyArgs = {};
+
+    this.dumpInspectorTimeStats = false;
+    this.dumpInspectorProtocolMessages = false;
+    this._initialized = false;
+}
+
+InspectorBackendClass.prototype = {
+    /**
+     * @return {number}
+     */
+    nextCallbackId: function()
+    {
+        return this._lastCallbackId++;
+    },
+
+    _wrap: function(callback, method)
+    {
+        var callbackId = this.nextCallbackId();
+        if (!callback)
+            callback = function() {};
+
+        this._callbacks[callbackId] = callback;
+        callback.methodName = method;
+        if (this.dumpInspectorTimeStats)
+            callback.sendRequestTime = Date.now();
+
+        return callbackId;
+    },
+
+    _getAgent: function(domain)
+    {
+        var agentName = domain + "Agent";
+        if (!window[agentName])
+            window[agentName] = {};
+        return window[agentName];
+    },
+
+    registerCommand: function(method, signature, replyArgs)
+    {
+        var domainAndMethod = method.split(".");
+        var agent = this._getAgent(domainAndMethod[0]);
+
+        agent[domainAndMethod[1]] = this._sendMessageToBackend.bind(this, method, signature);
+        agent[domainAndMethod[1]]["invoke"] = this._invoke.bind(this, method, signature);
+        this._replyArgs[method] = replyArgs;
+
+        this._initialized = true;
+    },
+
+    registerEnum: function(type, values)
+    {
+        var domainAndMethod = type.split(".");
+        var agent = this._getAgent(domainAndMethod[0]);
+
+        agent[domainAndMethod[1]] = values;
+
+        this._initialized = true;
+    },
+
+    registerEvent: function(eventName, params)
+    {
+        this._eventArgs[eventName] = params;
+
+        this._initialized = true;
+    },
+
+    _invoke: function(method, signature, args, callback)
+    {
+        this._wrapCallbackAndSendMessageObject(method, args, callback);
+    },
+
+    _sendMessageToBackend: function(method, signature, vararg)
+    {
+        var args = Array.prototype.slice.call(arguments, 2);
+        var callback = (args.length && typeof args[args.length - 1] === "function") ? args.pop() : null;
+
+        var params = {};
+        var hasParams = false;
+        for (var i = 0; i < signature.length; ++i) {
+            var param = signature[i];
+            var paramName = param["name"];
+            var typeName = param["type"];
+            var optionalFlag = param["optional"];
+
+            if (!args.length && !optionalFlag) {
+                console.error("Protocol Error: Invalid number of arguments for method '" + method + "' call. It must have the following arguments '" + JSON.stringify(signature) + "'.");
+                return;
+            }
+
+            var value = args.shift();
+            if (optionalFlag && typeof value === "undefined") {
+                continue;
+            }
+
+            if (typeof value !== typeName) {
+                console.error("Protocol Error: Invalid type of argument '" + paramName + "' for method '" + method + "' call. It must be '" + typeName + "' but it is '" + typeof value + "'.");
+                return;
+            }
+
+            params[paramName] = value;
+            hasParams = true;
+        }
+
+        if (args.length === 1 && !callback) {
+            if (typeof args[0] !== "undefined") {
+                console.error("Protocol Error: Optional callback argument for method '" + method + "' call must be a function but its type is '" + typeof args[0] + "'.");
+                return;
+            }
+        }
+
+        this._wrapCallbackAndSendMessageObject(method, hasParams ? params : null, callback);
+    },
+
+    _wrapCallbackAndSendMessageObject: function(method, params, callback)
+    {
+        var messageObject = {};
+        messageObject.method = method;
+        if (params)
+            messageObject.params = params;
+        messageObject.id = this._wrap(callback, method);
+
+        if (this.dumpInspectorProtocolMessages)
+            console.log("frontend: " + JSON.stringify(messageObject));
+
+        ++this._pendingResponsesCount;
+        this.sendMessageObjectToBackend(messageObject);
+    },
+
+    sendMessageObjectToBackend: function(messageObject)
+    {
+        if (this._disconnected)
+            return;
+        var message = JSON.stringify(messageObject);
+        InspectorFrontendHost.sendMessageToBackend(message);
+    },
+
+    disconnect: function()
+    {
+        this._disconnected = true;
+    },
+
+    registerDomainDispatcher: function(domain, dispatcher)
+    {
+        this._domainDispatchers[domain] = dispatcher;
+    },
+
+    dispatch: function(message)
+    {
+        if (this.dumpInspectorProtocolMessages)
+            console.log("backend: " + ((typeof message === "string") ? message : JSON.stringify(message)));
+
+        var messageObject = (typeof message === "string") ? JSON.parse(message) : message;
+
+        if ("id" in messageObject) { // just a response for some request
+            if (messageObject.error) {
+                if (messageObject.error.code !== -32000)
+                    this.reportProtocolError(messageObject);
+            }
+
+            var callback = this._callbacks[messageObject.id];
+            if (callback) {
+                var argumentsArray = [];
+                if (messageObject.result) {
+                    var paramNames = this._replyArgs[callback.methodName];
+                    if (paramNames) {
+                        for (var i = 0; i < paramNames.length; ++i)
+                            argumentsArray.push(messageObject.result[paramNames[i]]);
+                    }
+                }
+
+                var processingStartTime;
+                if (this.dumpInspectorTimeStats && callback.methodName)
+                    processingStartTime = Date.now();
+
+                argumentsArray.unshift(messageObject.error ? messageObject.error.message : null);
+                callback.apply(null, argumentsArray);
+                --this._pendingResponsesCount;
+                delete this._callbacks[messageObject.id];
+
+                if (this.dumpInspectorTimeStats && callback.methodName)
+                    console.log("time-stats: " + callback.methodName + " = " + (processingStartTime - callback.sendRequestTime) + " + " + (Date.now() - processingStartTime));
+            }
+
+            if (this._scripts && !this._pendingResponsesCount)
+                this.runAfterPendingDispatches();
+
+            return;
+        } else {
+            var method = messageObject.method.split(".");
+            var domainName = method[0];
+            var functionName = method[1];
+            if (!(domainName in this._domainDispatchers)) {
+                console.error("Protocol Error: the message is for non-existing domain '" + domainName + "'");
+                return;
+            }
+            var dispatcher = this._domainDispatchers[domainName];
+            if (!(functionName in dispatcher)) {
+                console.error("Protocol Error: Attempted to dispatch an unimplemented method '" + messageObject.method + "'");
+                return;
+            }
+
+            if (!this._eventArgs[messageObject.method]) {
+                console.error("Protocol Error: Attempted to dispatch an unspecified method '" + messageObject.method + "'");
+                return;
+            }
+
+            var params = [];
+            if (messageObject.params) {
+                var paramNames = this._eventArgs[messageObject.method];
+                for (var i = 0; i < paramNames.length; ++i)
+                    params.push(messageObject.params[paramNames[i]]);
+            }
+
+            var processingStartTime;
+            if (this.dumpInspectorTimeStats)
+                processingStartTime = Date.now();
+
+            dispatcher[functionName].apply(dispatcher, params);
+
+            if (this.dumpInspectorTimeStats)
+                console.log("time-stats: " + messageObject.method + " = " + (Date.now() - processingStartTime));
+        }
+    },
+
+    reportProtocolError: function(messageObject)
+    {
+        console.error("Request with id = " + messageObject.id + " failed. " + messageObject.error);
+    },
+
+    /**
+     * @param {string=} script
+     */
+    runAfterPendingDispatches: function(script)
+    {
+        if (!this._scripts)
+            this._scripts = [];
+
+        if (script)
+            this._scripts.push(script);
+
+        if (!this._pendingResponsesCount) {
+            var scripts = this._scripts;
+            this._scripts = []
+            for (var id = 0; id < scripts.length; ++id)
+                 scripts[id].call(this);
+        }
+    },
+
+    loadFromJSONIfNeeded: function(jsonUrl)
+    {
+        if (this._initialized)
+            return;
+
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", jsonUrl, false);
+        xhr.send(null);
+
+        var schema = JSON.parse(xhr.responseText);
+        var code = InspectorBackendClass._generateCommands(schema);
+        eval(code);
+    }
+}
+
+/**
+ * @param {*} schema
+ * @return {string}
+ */
+InspectorBackendClass._generateCommands = function(schema) {
+    var jsTypes = { integer: "number", array: "object" };
+    var rawTypes = {};
+    var result = [];
+
+    var domains = schema["domains"] || [];
+    for (var i = 0; i < domains.length; ++i) {
+        var domain = domains[i];
+        for (var j = 0; domain.types && j < domain.types.length; ++j) {
+            var type = domain.types[j];
+            rawTypes[domain.domain + "." + type.id] = jsTypes[type.type] || type.type;
+        }
+    }
+
+    function toUpperCase(groupIndex, group0, group1)
+    {
+        return [group0, group1][groupIndex].toUpperCase();
+    }
+    function generateEnum(enumName, items)
+    {
+        var members = []
+        for (var m = 0; m < items.length; ++m) {
+            var value = items[m];
+            var name = value.replace(/-(\w)/g, toUpperCase.bind(null, 1)).toTitleCase();
+            name = name.replace(/HTML|XML|WML|API/ig, toUpperCase.bind(null, 0));
+            members.push(name + ": \"" + value +"\"");
+        }
+        return "InspectorBackend.registerEnum(\"" + enumName + "\", {" + members.join(", ") + "});";
+    }
+
+    for (var i = 0; i < domains.length; ++i) {
+        var domain = domains[i];
+
+        var types = domain["types"] || [];
+        for (var j = 0; j < types.length; ++j) {
+            var type = types[j];
+            if ((type["type"] === "string") && type["enum"])
+                result.push(generateEnum(domain.domain + "." + type.id, type["enum"]));
+            else if (type["type"] === "object") {
+                var properties = type["properties"] || [];
+                for (var k = 0; k < properties.length; ++k) {
+                    var property = properties[k];
+                    if ((property["type"] === "string") && property["enum"])
+                        result.push(generateEnum(domain.domain + "." + type.id + property["name"].toTitleCase(), property["enum"]));
+                }
+            }
+        }
+
+        var commands = domain["commands"] || [];
+        for (var j = 0; j < commands.length; ++j) {
+            var command = commands[j];
+            var parameters = command["parameters"];
+            var paramsText = [];
+            for (var k = 0; parameters && k < parameters.length; ++k) {
+                var parameter = parameters[k];
+
+                var type;
+                if (parameter.type)
+                    type = jsTypes[parameter.type] || parameter.type;
+                else {
+                    var ref = parameter["$ref"];
+                    if (ref.indexOf(".") !== -1)
+                        type = rawTypes[ref];
+                    else
+                        type = rawTypes[domain.domain + "." + ref];
+                }
+
+                var text = "{\"name\": \"" + parameter.name + "\", \"type\": \"" + type + "\", \"optional\": " + (parameter.optional ? "true" : "false") + "}";
+                paramsText.push(text);
+            }
+
+            var returnsText = [];
+            var returns = command["returns"] || [];
+            for (var k = 0; k < returns.length; ++k) {
+                var parameter = returns[k];
+                returnsText.push("\"" + parameter.name + "\"");
+            }
+            result.push("InspectorBackend.registerCommand(\"" + domain.domain + "." + command.name + "\", [" + paramsText.join(", ") + "], [" + returnsText.join(", ") + "]);");
+        }
+
+        for (var j = 0; domain.events && j < domain.events.length; ++j) {
+            var event = domain.events[j];
+            var paramsText = [];
+            for (var k = 0; event.parameters && k < event.parameters.length; ++k) {
+                var parameter = event.parameters[k];
+                paramsText.push("\"" + parameter.name + "\"");
+            }
+            result.push("InspectorBackend.registerEvent(\"" + domain.domain + "." + event.name + "\", [" + paramsText.join(", ") + "]);");
+        }
+
+        result.push("InspectorBackend.register" + domain.domain + "Dispatcher = InspectorBackend.registerDomainDispatcher.bind(InspectorBackend, \"" + domain.domain + "\");");
+    }
+    return result.join("\n");
+}
+
+InspectorBackend = new InspectorBackendClass();
diff --git a/Source/devtools/front_end/InspectorFrontendAPI.js b/Source/devtools/front_end/InspectorFrontendAPI.js
new file mode 100644
index 0000000..9bfef22
--- /dev/null
+++ b/Source/devtools/front_end/InspectorFrontendAPI.js
@@ -0,0 +1,168 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+var InspectorFrontendAPI = {
+    _pendingCommands: [],
+
+    setAttachedWindow: function(side)
+    {
+    },
+
+    setDockSide: function(side)
+    {
+        if (WebInspector.dockController)
+            WebInspector.dockController.setDockSide(side);
+    },
+
+    showConsole: function()
+    {
+        WebInspector.showPanel("console");
+    },
+
+    showMainResourceForFrame: function(frameId)
+    {
+        // FIXME: Implement this to show the source code for the main resource of a given frame.
+    },
+
+    showResources: function()
+    {
+        WebInspector.showPanel("resources");
+    },
+
+    setDockingUnavailable: function(unavailable)
+    {
+        WebInspector.setDockingUnavailable(unavailable);
+    },
+
+    enterInspectElementMode: function()
+    {
+        WebInspector.showPanel("elements");
+
+        if (WebInspector.inspectElementModeController)
+            WebInspector.inspectElementModeController.toggleSearch();
+    },
+
+    fileSystemsLoaded: function(fileSystems)
+    {
+        WebInspector.isolatedFileSystemDispatcher.fileSystemsLoaded(fileSystems);
+    },
+
+    fileSystemRemoved: function(fileSystemPath)
+    {
+        WebInspector.isolatedFileSystemDispatcher.fileSystemRemoved(fileSystemPath);
+    },
+
+    fileSystemAdded: function(errorMessage, fileSystem)
+    {
+        WebInspector.isolatedFileSystemDispatcher.fileSystemAdded(errorMessage, fileSystem);
+    },
+
+    savedURL: function(url)
+    {
+        WebInspector.fileManager.savedURL(url);
+    },
+
+    appendedToURL: function(url)
+    {
+        WebInspector.fileManager.appendedToURL(url);
+    },
+
+    setToolbarColors: function(backgroundColor, color)
+    {
+        WebInspector.setToolbarColors(backgroundColor, color);
+    },
+
+    evaluateForTest: function(callId, script)
+    {
+        WebInspector.evaluateForTestInFrontend(callId, script);
+    },
+
+    dispatch: function(signature)
+    {
+        if (InspectorFrontendAPI._isLoaded) {
+            var methodName = signature.shift();
+            return InspectorFrontendAPI[methodName].apply(InspectorFrontendAPI, signature);
+        }
+        InspectorFrontendAPI._pendingCommands.push(signature);
+    },
+
+    dispatchQueryParameters: function()
+    {
+        if ("dispatch" in WebInspector.queryParamsObject)
+            InspectorFrontendAPI.dispatch(JSON.parse(window.decodeURI(WebInspector.queryParamsObject["dispatch"])));
+    },
+
+    /**
+     * @param {string} url
+     */
+    loadTimelineFromURL: function(url) 
+    {
+        /** @type {WebInspector.TimelinePanel} */ (WebInspector.showPanel("timeline")).loadFromURL(url);
+    },
+
+    loadCompleted: function()
+    {
+        InspectorFrontendAPI._isLoaded = true;
+        for (var i = 0; i < InspectorFrontendAPI._pendingCommands.length; ++i)
+            InspectorFrontendAPI.dispatch(InspectorFrontendAPI._pendingCommands[i]);
+        InspectorFrontendAPI._pendingCommands = [];
+        if (window.opener)
+            window.opener.postMessage(["loadCompleted"], "*");
+    },
+
+    contextMenuItemSelected: function(id)
+    {
+        WebInspector.contextMenuItemSelected(id);
+    },
+
+    contextMenuCleared: function()
+    {
+        WebInspector.contextMenuCleared();
+    },
+
+    dispatchMessageAsync: function(messageObject)
+    {
+        WebInspector.dispatch(messageObject);
+    },
+
+    dispatchMessage: function(messageObject)
+    {
+        InspectorBackend.dispatch(messageObject);
+    }
+}
+
+if (window.opener && window.dispatchStandaloneTestRunnerMessages) {
+    function onMessageFromOpener(event)
+    {
+        if (event.source === window.opener)
+            InspectorFrontendAPI.dispatch(event.data);
+    }
+    window.addEventListener("message", onMessageFromOpener, true);
+}
diff --git a/Source/devtools/front_end/InspectorFrontendHostStub.js b/Source/devtools/front_end/InspectorFrontendHostStub.js
new file mode 100644
index 0000000..ee7afa5
--- /dev/null
+++ b/Source/devtools/front_end/InspectorFrontendHostStub.js
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+if (!window.InspectorFrontendHost) {
+
+/**
+ * @constructor
+ * @implements {InspectorFrontendHostAPI}
+ */
+WebInspector.InspectorFrontendHostStub = function()
+{
+    this._attachedWindowHeight = 0;
+    this.isStub = true;
+    this._fileBuffers = {};
+    WebInspector.documentCopyEventFired = this.documentCopy.bind(this);
+}
+
+WebInspector.InspectorFrontendHostStub.prototype = {
+    getSelectionBackgroundColor: function()
+    {
+        return "#6e86ff";
+    },
+
+    getSelectionForegroundColor: function()
+    {
+        return "#ffffff";
+    },
+
+    platform: function()
+    {
+        var match = navigator.userAgent.match(/Windows NT/);
+        if (match)
+            return "windows";
+        match = navigator.userAgent.match(/Mac OS X/);
+        if (match)
+            return "mac";
+        return "linux";
+    },
+
+    port: function()
+    {
+        return "unknown";
+    },
+
+    bringToFront: function()
+    {
+        this._windowVisible = true;
+    },
+
+    closeWindow: function()
+    {
+        this._windowVisible = false;
+    },
+
+    requestSetDockSide: function(side)
+    {
+        InspectorFrontendAPI.setDockSide(side);
+    },
+
+    setAttachedWindowHeight: function(height)
+    {
+    },
+
+    moveWindowBy: function(x, y)
+    {
+    },
+
+    setInjectedScriptForOrigin: function(origin, script)
+    {
+    },
+
+    loaded: function()
+    {
+    },
+
+    localizedStringsURL: function()
+    {
+        return undefined;
+    },
+
+    inspectedURLChanged: function(url)
+    {
+        document.title = WebInspector.UIString(Preferences.applicationTitle, url);
+    },
+
+    documentCopy: function(event)
+    {
+        if (!this._textToCopy)
+            return;
+        event.clipboardData.setData("text", this._textToCopy);
+        event.preventDefault();
+        delete this._textToCopy;
+    },
+
+    copyText: function(text)
+    {
+        this._textToCopy = text;
+        if (!document.execCommand("copy")) {
+            var screen = new WebInspector.ClipboardAccessDeniedScreen();
+            screen.showModal();
+        }
+    },
+
+    openInNewTab: function(url)
+    {
+        window.open(url, "_blank");
+    },
+
+    canSave: function()
+    {
+        return true;
+    },
+
+    save: function(url, content, forceSaveAs)
+    {
+        if (this._fileBuffers[url])
+            throw new Error("Concurrent file modification denied.");
+
+        this._fileBuffers[url] = [content];
+        setTimeout(WebInspector.fileManager.savedURL.bind(WebInspector.fileManager, url), 0);
+    },
+
+    append: function(url, content)
+    {
+        var buffer = this._fileBuffers[url];
+        if (!buffer)
+            throw new Error("File is not open for write yet.");
+
+        buffer.push(content);
+        setTimeout(WebInspector.fileManager.appendedToURL.bind(WebInspector.fileManager, url), 0);
+    },
+
+    close: function(url)
+    {
+        var content = this._fileBuffers[url];
+        delete this._fileBuffers[url];
+
+        if (!content)
+            return;
+
+        var lastSlashIndex = url.lastIndexOf("/");
+        var fileNameSuffix = (lastSlashIndex === -1) ? url : url.substring(lastSlashIndex + 1);
+
+        var blob = new Blob(content, { type: "application/octet-stream" });
+        var objectUrl = window.URL.createObjectURL(blob);
+        window.location = objectUrl + "#" + fileNameSuffix;
+
+        function cleanup()
+        {
+            window.URL.revokeObjectURL(objectUrl);
+        }
+        setTimeout(cleanup, 0);
+    },
+
+    sendMessageToBackend: function(message)
+    {
+    },
+
+    recordActionTaken: function(actionCode)
+    {
+    },
+
+    recordPanelShown: function(panelCode)
+    {
+    },
+
+    recordSettingChanged: function(settingCode)
+    {
+    },
+
+    loadResourceSynchronously: function(url)
+    {
+        return loadXHR(url);
+    },
+
+    supportsFileSystems: function()
+    {
+        return false;
+    },
+
+    requestFileSystems: function()
+    {
+    },
+
+    addFileSystem: function()
+    {
+    },
+
+    removeFileSystem: function(fileSystemPath)
+    {
+    },
+
+    isolatedFileSystem: function(fileSystemId, registeredName)
+    {
+        return null;
+    },
+
+    setZoomFactor: function(zoom)
+    {
+    },
+
+    isUnderTest: function()
+    {
+        return false;
+    }
+}
+
+InspectorFrontendHost = new WebInspector.InspectorFrontendHostStub();
+
+/**
+ * @constructor
+ * @extends {WebInspector.HelpScreen}
+ */
+WebInspector.ClipboardAccessDeniedScreen = function()
+{
+    WebInspector.HelpScreen.call(this, WebInspector.UIString("Clipboard access is denied"));
+    var platformMessage = WebInspector.UIString("You need to install a Chrome extension that grants clipboard access to Developer Tools.");
+    if (platformMessage) {
+        var p = this.contentElement.createChild("p");
+        p.addStyleClass("help-section");
+        p.textContent = platformMessage;
+    }
+}
+
+WebInspector.ClipboardAccessDeniedScreen.prototype = {
+    __proto__: WebInspector.HelpScreen.prototype
+}
+
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HelpScreen}
+ */
+WebInspector.RemoteDebuggingTerminatedScreen = function(reason)
+{
+    WebInspector.HelpScreen.call(this, WebInspector.UIString("Detached from the target"));
+    var p = this.contentElement.createChild("p");
+    p.addStyleClass("help-section");
+    p.createChild("span").textContent = "Remote debugging has been terminated with reason: ";
+    p.createChild("span", "error-message").textContent = reason;
+    p.createChild("br");
+    p.createChild("span").textContent = "Please re-attach to the new target.";
+}
+
+WebInspector.RemoteDebuggingTerminatedScreen.prototype = {
+    __proto__: WebInspector.HelpScreen.prototype
+}
diff --git a/Source/devtools/front_end/InspectorView.js b/Source/devtools/front_end/InspectorView.js
new file mode 100644
index 0000000..34ef4be
--- /dev/null
+++ b/Source/devtools/front_end/InspectorView.js
@@ -0,0 +1,287 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.InspectorView = function()
+{
+    WebInspector.View.call(this);
+    this.markAsRoot();
+    this.element.id = "main-panels";
+    this.element.setAttribute("spellcheck", false);
+    this._history = [];
+    this._historyIterator = -1;
+    document.addEventListener("keydown", this._keyDown.bind(this), false);
+    document.addEventListener("keypress", this._keyPress.bind(this), false);
+    this._panelOrder = [];
+    this._panelDescriptors = {};
+
+    // Windows and Mac have two different definitions of '[' and ']', so accept both of each.
+    this._openBracketIdentifiers = ["U+005B", "U+00DB"].keySet();
+    this._closeBracketIdentifiers = ["U+005D", "U+00DD"].keySet();
+    this._footerElementContainer = this.element.createChild("div", "inspector-footer status-bar hidden");
+    this._panelsElement = this.element.createChild("div", "fill");
+}
+
+WebInspector.InspectorView.Events = {
+    PanelSelected: "PanelSelected"
+}
+
+WebInspector.InspectorView.prototype = {
+    /**
+     * @param {WebInspector.PanelDescriptor} panelDescriptor
+     */
+    addPanel: function(panelDescriptor)
+    {
+        this._panelOrder.push(panelDescriptor.name());
+        this._panelDescriptors[panelDescriptor.name()] = panelDescriptor;
+        WebInspector.toolbar.addPanel(panelDescriptor);
+    },
+
+    /**
+     * @param {string} panelName
+     * @return {?WebInspector.Panel}
+     */
+    panel: function(panelName)
+    {
+        var panelDescriptor = this._panelDescriptors[panelName];
+        if (!panelDescriptor && this._panelOrder.length)
+            panelDescriptor = this._panelDescriptors[this._panelOrder[0]];
+        return panelDescriptor ? panelDescriptor.panel() : null;
+    },
+
+    /**
+     * @param {string} panelName
+     * @return {?WebInspector.Panel}
+     */
+    showPanel: function(panelName)
+    {
+        var panel = this.panel(panelName);
+        if (panel)
+            this.setCurrentPanel(panel);
+        return panel;
+    },
+
+    /**
+     * @return {WebInspector.Panel}
+     */
+    currentPanel: function()
+    {
+        return this._currentPanel;
+    },
+    
+    /**
+     * @param {WebInspector.Panel} x
+     */
+    setCurrentPanel: function(x)
+    {
+        if (this._currentPanel === x)
+            return;
+
+        // FIXME: remove search controller.
+        WebInspector.searchController.cancelSearch();
+
+        if (this._currentPanel)
+            this._currentPanel.detach();
+
+        this._currentPanel = x;
+
+        if (x) {
+            x.show();
+            this.dispatchEventToListeners(WebInspector.InspectorView.Events.PanelSelected);
+        }
+        for (var panelName in WebInspector.panels) {
+            if (WebInspector.panels[panelName] === x) {
+                WebInspector.settings.lastActivePanel.set(panelName);
+                this._pushToHistory(panelName);
+                WebInspector.userMetrics.panelShown(panelName);
+            }
+        }
+    },
+
+    /**
+     * @return {Element}
+     */
+    defaultFocusedElement: function()
+    {
+        return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null;
+    },
+
+    _keyPress: function(event)
+    {
+        // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress.
+        // Any charCode < 32 is not going to be a valid keypress.
+        if (event.charCode < 32 && WebInspector.isWin())
+            return;
+        clearTimeout(this._keyDownTimer);
+        delete this._keyDownTimer;
+    },
+
+    _keyDown: function(event)
+    {
+        if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event))
+            return;
+
+        // Ctrl/Cmd + 1-9 should show corresponding panel.
+        var panelShortcutEnabled = WebInspector.settings.shortcutPanelSwitch.get();
+        if (panelShortcutEnabled && !event.shiftKey && !event.altKey && event.keyCode > 0x30 && event.keyCode < 0x3A) {
+            var panelName = this._panelOrder[event.keyCode - 0x31];
+            if (panelName) {
+                this.showPanel(panelName);
+                event.consume(true);
+            }
+            return;
+        }
+
+        // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[,
+        // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one.
+        // If there is, we cancel the timer and do not consider this a panel switch.
+        if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) {
+            this._keyDownInternal(event);
+            return;
+        }
+
+        this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0);
+    },
+
+    _keyDownInternal: function(event)
+    {
+        if (this._openBracketIdentifiers[event.keyIdentifier]) {
+            var isRotateLeft = !event.shiftKey && !event.altKey;
+            if (isRotateLeft) {
+                var index = this._panelOrder.indexOf(this.currentPanel().name);
+                index = (index === 0) ? this._panelOrder.length - 1 : index - 1;
+                this.showPanel(this._panelOrder[index]);
+                event.consume(true);
+                return;
+            }
+
+            var isGoBack = event.altKey;
+            if (isGoBack && this._canGoBackInHistory()) {
+                this._goBackInHistory();
+                event.consume(true);
+            }
+            return;
+        }
+
+        if (this._closeBracketIdentifiers[event.keyIdentifier]) {
+            var isRotateRight = !event.shiftKey && !event.altKey;
+            if (isRotateRight) {
+                var index = this._panelOrder.indexOf(this.currentPanel().name);
+                index = (index + 1) % this._panelOrder.length;
+                this.showPanel(this._panelOrder[index]);
+                event.consume(true);
+                return;
+            }
+
+            var isGoForward = event.altKey;
+            if (isGoForward && this._canGoForwardInHistory()) {
+                this._goForwardInHistory();
+                event.consume(true);
+            }
+            return;
+        }
+    },
+
+    _canGoBackInHistory: function()
+    {
+        return this._historyIterator > 0;
+    },
+
+    _goBackInHistory: function()
+    {
+        this._inHistory = true;
+        this.setCurrentPanel(WebInspector.panels[this._history[--this._historyIterator]]);
+        delete this._inHistory;
+    },
+
+    _canGoForwardInHistory: function()
+    {
+        return this._historyIterator < this._history.length - 1;
+    },
+
+    _goForwardInHistory: function()
+    {
+        this._inHistory = true;
+        this.setCurrentPanel(WebInspector.panels[this._history[++this._historyIterator]]);
+        delete this._inHistory;
+    },
+
+    _pushToHistory: function(panelName)
+    {
+        if (this._inHistory)
+            return;
+
+        this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
+        if (!this._history.length || this._history[this._history.length - 1] !== panelName)
+            this._history.push(panelName);
+        this._historyIterator = this._history.length - 1;
+    },
+
+    panelsElement: function()
+    {
+        return this._panelsElement;
+    },
+
+    /**
+     * @param {Element?} element
+     */
+    setFooterElement: function(element)
+    {
+        if (element) {
+            this._footerElementContainer.removeStyleClass("hidden");
+            this._footerElementContainer.appendChild(element);
+            this._panelsElement.style.bottom = this._footerElementContainer.offsetHeight + "px";
+        } else {
+            this._footerElementContainer.addStyleClass("hidden");
+            this._footerElementContainer.removeChildren();
+            this._panelsElement.style.bottom = 0;
+        }
+        this.doResize();
+    },
+
+    /**
+     * @param {WebInspector.Panel} panel
+     */
+    showPanelForAnchorNavigation: function(panel)
+    {
+        WebInspector.searchController.disableSearchUntilExplicitAction();
+        this.setCurrentPanel(panel);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @type {WebInspector.InspectorView}
+ */
+WebInspector.inspectorView = null;
diff --git a/Source/devtools/front_end/IsolatedFileSystem.js b/Source/devtools/front_end/IsolatedFileSystem.js
new file mode 100644
index 0000000..ea638ac
--- /dev/null
+++ b/Source/devtools/front_end/IsolatedFileSystem.js
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.IsolatedFileSystemManager} manager
+ * @param {string} path
+ */
+WebInspector.IsolatedFileSystem = function(manager, path, name, rootURL)
+{
+    this._manager = manager;
+    this._path = path;
+    this._name = name;
+    this._rootURL = rootURL;
+}
+
+WebInspector.IsolatedFileSystem.errorMessage = function(error)
+{
+    var msg;
+    switch (error.code) {
+    case FileError.QUOTA_EXCEEDED_ERR:
+        msg = "QUOTA_EXCEEDED_ERR";
+        break;
+    case FileError.NOT_FOUND_ERR:
+        msg = "NOT_FOUND_ERR";
+        break;
+    case FileError.SECURITY_ERR:
+        msg = "SECURITY_ERR";
+        break;
+    case FileError.INVALID_MODIFICATION_ERR:
+        msg = "INVALID_MODIFICATION_ERR";
+        break;
+    case FileError.INVALID_STATE_ERR:
+        msg = "INVALID_STATE_ERR";
+        break;
+    default:
+        msg = "Unknown Error";
+        break;
+    };
+
+    return "File system error: " + msg;
+}
+
+WebInspector.IsolatedFileSystem.prototype = {
+    /**
+     * @return {string}
+     */
+    path: function()
+    {
+        return this._path;
+    },
+
+    /**
+     * @return {string}
+     */
+    name: function()
+    {
+        return this._name;
+    },
+
+    /**
+     * @return {string}
+     */
+    rootURL: function()
+    {
+        return this._rootURL;
+    },
+
+    /**
+     * @param {function(DOMFileSystem)} callback
+     */
+    _requestFileSystem: function(callback)
+    {
+        this._manager.requestDOMFileSystem(this._path, callback);
+    },
+
+    /**
+     * @param {string} path
+     * @param {function(string)} callback
+     */
+    requestFilesRecursive: function(path, callback)
+    {
+        this._requestFileSystem(fileSystemLoaded.bind(this));
+
+        var domFileSystem;
+        /**
+         * @param {DOMFileSystem} fs
+         */
+        function fileSystemLoaded(fs)
+        {
+            domFileSystem = fs;
+            this._requestEntries(domFileSystem, path, innerCallback.bind(this));
+        }
+
+        /**
+         * @param {Array.<FileEntry>} entries
+         */
+        function innerCallback(entries)
+        {
+            for (var i = 0; i < entries.length; ++i) {
+                var entry = entries[i];
+                if (!entry.isDirectory)
+                    callback(entry.fullPath);
+                else
+                    this._requestEntries(domFileSystem, entry.fullPath, innerCallback.bind(this));
+            }
+        }
+    },
+
+    /**
+     * @param {string} path
+     * @param {function(?string)} callback
+     */
+    requestFileContent: function(path, callback)
+    {
+        this._requestFileSystem(fileSystemLoaded.bind(this));
+
+        /**
+         * @param {DOMFileSystem} domFileSystem
+         */
+        function fileSystemLoaded(domFileSystem)
+        {
+            domFileSystem.root.getFile(path, null, fileEntryLoaded, errorHandler);
+        }
+
+        /**
+         * @param {FileEntry} entry
+         */
+        function fileEntryLoaded(entry)
+        {
+            entry.file(fileLoaded, errorHandler);
+        }
+
+        /**
+         * @param {!Blob} file
+         */
+        function fileLoaded(file)
+        {
+            var reader = new FileReader();
+            reader.onloadend = readerLoadEnd;
+            reader.readAsText(file);
+        }
+
+        /**
+         * @this {FileReader}
+         */
+        function readerLoadEnd()
+        {
+            callback(/** @type {string} */ (this.result));
+        }
+
+        function errorHandler(error)
+        {
+            if (error.code === FileError.NOT_FOUND_ERR) {
+                callback(null);
+                return;
+            }
+
+            var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
+            console.error(errorMessage + " when getting content for file '" + (this._path + "/" + path) + "'");
+            callback(null);
+        }
+    },
+
+    /**
+     * @param {string} path
+     * @param {string} content
+     * @param {function()} callback
+     */
+    setFileContent: function(path, content, callback)
+    {
+        this._requestFileSystem(fileSystemLoaded);
+
+        /**
+         * @param {DOMFileSystem} domFileSystem
+         */
+        function fileSystemLoaded(domFileSystem)
+        {
+            domFileSystem.root.getFile(path, { create: true }, fileEntryLoaded, errorHandler);
+        }
+
+        /**
+         * @param {FileEntry} entry
+         */
+        function fileEntryLoaded(entry)
+        {
+            entry.createWriter(fileWriterCreated, errorHandler);
+        }
+
+        /**
+         * @param {FileWriter} fileWriter
+         */
+        function fileWriterCreated(fileWriter)
+        {
+            fileWriter.onerror = errorHandler;
+            fileWriter.onwriteend = fileTruncated;
+            fileWriter.truncate(0);
+
+            function fileTruncated()
+            {
+                fileWriter.onwriteend = writerEnd;
+                var blob = new Blob([content], { type: "text/plain" });
+                fileWriter.write(blob);
+            }
+        }
+
+        function writerEnd()
+        {
+            callback();
+        }
+
+        function errorHandler(error)
+        {
+            var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
+            console.error(errorMessage + " when setting content for file '" + (this._path + "/" + path) + "'");
+            callback();
+        }
+    },
+
+    /**
+     * @param {DirectoryEntry} dirEntry
+     * @param {function(Array.<FileEntry>)} callback
+     */
+    _readDirectory: function(dirEntry, callback)
+    {
+        var dirReader = dirEntry.createReader();
+        var entries = [];
+
+        function innerCallback(results)
+        {
+            if (!results.length)
+                callback(entries.sort());
+            else {
+                entries = entries.concat(toArray(results));
+                dirReader.readEntries(innerCallback, errorHandler);
+            }
+        }
+
+        function toArray(list)
+        {
+            return Array.prototype.slice.call(list || [], 0);
+        }    
+
+        dirReader.readEntries(innerCallback, errorHandler);
+
+        function errorHandler(error)
+        {
+            var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
+            console.error(errorMessage + " when reading directory '" + dirEntry.fullPath + "'");
+            callback([]);
+        }
+    },
+
+    /**
+     * @param {DOMFileSystem} domFileSystem
+     * @param {string} path
+     * @param {function(Array.<FileEntry>)} callback
+     */
+    _requestEntries: function(domFileSystem, path, callback)
+    {
+        domFileSystem.root.getDirectory(path, null, innerCallback.bind(this), errorHandler);
+
+        function innerCallback(dirEntry)
+        {
+            this._readDirectory(dirEntry, callback)
+        }
+
+        function errorHandler(error)
+        {
+            var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
+            console.error(errorMessage + " when requesting entry '" + path + "'");
+            callback([]);
+        }
+    }
+}
diff --git a/Source/devtools/front_end/IsolatedFileSystemManager.js b/Source/devtools/front_end/IsolatedFileSystemManager.js
new file mode 100644
index 0000000..4e9f293
--- /dev/null
+++ b/Source/devtools/front_end/IsolatedFileSystemManager.js
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.IsolatedFileSystemManager = function()
+{
+    /** @type {!Object.<string, WebInspector.IsolatedFileSystem>} */
+    this._fileSystems = {};
+    /** @type {Object.<string, Array.<function(DOMFileSystem)>>} */
+    this._pendingFileSystemRequests = {};
+    this._fileSystemMapping = new WebInspector.FileSystemMappingImpl();
+
+    if (this.supportsFileSystems())
+        this._requestFileSystems();
+}
+
+/** @typedef {{fileSystemName: string, rootURL: string, fileSystemPath: string}} */
+WebInspector.IsolatedFileSystemManager.FileSystem;
+
+WebInspector.IsolatedFileSystemManager.Events = {
+    FileSystemAdded: "FileSystemAdded",
+    FileSystemRemoved: "FileSystemRemoved"
+}
+
+WebInspector.IsolatedFileSystemManager.prototype = {
+    /**
+     * @return {WebInspector.FileSystemMapping}
+     */
+    mapping: function()
+    {
+        return this._fileSystemMapping;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    supportsFileSystems: function()
+    {
+        return InspectorFrontendHost.supportsFileSystems();
+    },
+
+    _requestFileSystems: function()
+    {
+        console.assert(!this._loaded);
+        InspectorFrontendHost.requestFileSystems();
+    },
+
+    /**
+     * @param {function(?string)} callback
+     */
+    addFileSystem: function(callback)
+    {
+        this._selectFileSystemPathCallback = callback;
+        InspectorFrontendHost.addFileSystem();
+    },
+
+    /**
+     * @param {function()} callback
+     */
+    removeFileSystem: function(fileSystemPath, callback)
+    {
+        this._removeFileSystemCallback = callback;
+        InspectorFrontendHost.removeFileSystem(fileSystemPath);
+    },
+
+    /**
+     * @param {Array.<WebInspector.IsolatedFileSystemManager.FileSystem>} fileSystems
+     */
+    _fileSystemsLoaded: function(fileSystems)
+    {
+        for (var i = 0; i < fileSystems.length; ++i)
+            this._innerAddFileSystem(fileSystems[i]);
+        this._loaded = true;
+        this._processPendingFileSystemRequests();
+    },
+
+    /**
+     * @param {WebInspector.IsolatedFileSystemManager.FileSystem} fileSystem
+     */
+    _innerAddFileSystem: function(fileSystem)
+    {
+        var fileSystemPath = fileSystem.fileSystemPath;
+        this._fileSystemMapping.addFileSystemMapping(fileSystemPath);
+        var isolatedFileSystem = new WebInspector.IsolatedFileSystem(this, fileSystemPath, fileSystem.fileSystemName, fileSystem.rootURL);
+        this._fileSystems[fileSystemPath] = isolatedFileSystem;
+        this.dispatchEventToListeners(WebInspector.IsolatedFileSystemManager.Events.FileSystemAdded, isolatedFileSystem);
+    },
+
+    /**
+     * @return {Array.<string>}
+     */
+    _fileSystemPaths: function()
+    {
+        return Object.keys(this._fileSystems);
+    },
+
+    _processPendingFileSystemRequests: function()
+    {
+        for (var fileSystemPath in this._pendingFileSystemRequests) {
+            var callbacks = this._pendingFileSystemRequests[fileSystemPath];
+            for (var i = 0; i < callbacks.length; ++i)
+                callbacks[i](this._isolatedFileSystem(fileSystemPath));
+        }
+        delete this._pendingFileSystemRequests;
+    },
+
+    /**
+     * @param {string} errorMessage
+     * @param {WebInspector.IsolatedFileSystemManager.FileSystem} fileSystem
+     */
+    _fileSystemAdded: function(errorMessage, fileSystem)
+    {
+        var fileSystemPath;
+        if (errorMessage)
+            WebInspector.showErrorMessage(errorMessage)
+        else if (fileSystem) {
+            this._innerAddFileSystem(fileSystem);
+            fileSystemPath = fileSystem.fileSystemPath;
+        }
+
+        if (this._selectFileSystemPathCallback) {
+            this._selectFileSystemPathCallback(fileSystemPath);
+            delete this._selectFileSystemPathCallback;
+        }
+    },
+
+    /**
+     * @param {string} fileSystemPath
+     */
+    _fileSystemRemoved: function(fileSystemPath)
+    {
+        this._fileSystemMapping.removeFileSystemMapping(fileSystemPath);
+        var isolatedFileSystem = this._fileSystems[fileSystemPath];
+        delete this._fileSystems[fileSystemPath];
+        if (this._removeFileSystemCallback) {
+            this._removeFileSystemCallback(fileSystemPath);
+            delete this._removeFileSystemCallback;
+        }
+        this.dispatchEventToListeners(WebInspector.IsolatedFileSystemManager.Events.FileSystemRemoved, isolatedFileSystem);
+    },
+
+    /**
+     * @param {string} fileSystemPath
+     * @return {DOMFileSystem}
+     */
+    _isolatedFileSystem: function(fileSystemPath)
+    {
+        var fileSystem = this._fileSystems[fileSystemPath];
+        if (!fileSystem)
+            return null;
+        if (!InspectorFrontendHost.isolatedFileSystem)
+            return null;
+        return InspectorFrontendHost.isolatedFileSystem(fileSystem.name(), fileSystem.rootURL());
+    },
+
+    /**
+     * @param {string} fileSystemPath
+     * @param {function(DOMFileSystem)} callback
+     */
+    requestDOMFileSystem: function(fileSystemPath, callback)
+    {
+        if (!this._loaded) {
+            if (!this._pendingFileSystemRequests[fileSystemPath])
+                this._pendingFileSystemRequests[fileSystemPath] = this._pendingFileSystemRequests[fileSystemPath] || [];
+            this._pendingFileSystemRequests[fileSystemPath].push(callback);
+            return;
+        }
+        callback(this._isolatedFileSystem(fileSystemPath));
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @type {?WebInspector.IsolatedFileSystemManager}
+ */
+WebInspector.isolatedFileSystemManager = null;
+
+/**
+ * @constructor
+ * @param {WebInspector.IsolatedFileSystemManager} IsolatedFileSystemManager
+ */
+WebInspector.IsolatedFileSystemDispatcher = function(IsolatedFileSystemManager)
+{
+    this._IsolatedFileSystemManager = IsolatedFileSystemManager;
+}
+
+WebInspector.IsolatedFileSystemDispatcher.prototype = {
+    /**
+     * @param {Array.<WebInspector.IsolatedFileSystemManager.FileSystem>} fileSystems
+     */
+    fileSystemsLoaded: function(fileSystems)
+    {
+        this._IsolatedFileSystemManager._fileSystemsLoaded(fileSystems);
+    },
+
+    /**
+     * @param {string} fileSystemPath
+     */
+    fileSystemRemoved: function(fileSystemPath)
+    {
+        this._IsolatedFileSystemManager._fileSystemRemoved(fileSystemPath);
+    },
+
+    /**
+     * @param {string} errorMessage
+     * @param {WebInspector.IsolatedFileSystemManager.FileSystem} fileSystem
+     */
+    fileSystemAdded: function(errorMessage, fileSystem)
+    {
+        this._IsolatedFileSystemManager._fileSystemAdded(errorMessage, fileSystem);
+    }
+}
+
+/**
+ * @type {?WebInspector.IsolatedFileSystemDispatcher}
+ */
+WebInspector.isolatedFileSystemDispatcher = null;
diff --git a/Source/devtools/front_end/JSHeapSnapshot.js b/Source/devtools/front_end/JSHeapSnapshot.js
new file mode 100644
index 0000000..72c167f
--- /dev/null
+++ b/Source/devtools/front_end/JSHeapSnapshot.js
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshot}
+ */
+WebInspector.JSHeapSnapshot = function(profile)
+{
+    this._nodeFlags = { // bit flags
+        canBeQueried: 1,
+        detachedDOMTreeNode: 2,
+        pageObject: 4, // The idea is to track separately the objects owned by the page and the objects owned by debugger.
+
+        visitedMarkerMask: 0x0ffff, // bits: 0,1111,1111,1111,1111
+        visitedMarker:     0x10000  // bits: 1,0000,0000,0000,0000
+    };
+    WebInspector.HeapSnapshot.call(this, profile);
+}
+
+WebInspector.JSHeapSnapshot.prototype = {
+    createNode: function(nodeIndex)
+    {
+        return new WebInspector.JSHeapSnapshotNode(this, nodeIndex);
+    },
+
+    createEdge: function(edges, edgeIndex)
+    {
+        return new WebInspector.JSHeapSnapshotEdge(this, edges, edgeIndex);
+    },
+
+    createRetainingEdge: function(retainedNodeIndex, retainerIndex)
+    {
+        return new WebInspector.JSHeapSnapshotRetainerEdge(this, retainedNodeIndex, retainerIndex);
+    },
+
+    classNodesFilter: function()
+    {
+        function filter(node)
+        {
+            return node.isUserObject();
+        }
+        return filter;
+    },
+
+    containmentEdgesFilter: function(showHiddenData)
+    {
+        function filter(edge) {
+            if (edge.isInvisible())
+                return false;
+            if (showHiddenData)
+                return true;
+            return !edge.isHidden() && !edge.node().isHidden();
+        }
+        return filter;
+    },
+
+    retainingEdgesFilter: function(showHiddenData)
+    {
+        var containmentEdgesFilter = this.containmentEdgesFilter(showHiddenData);
+        function filter(edge) {
+            if (!containmentEdgesFilter(edge))
+                return false;
+            return edge.node().id() !== 1 && !edge.node().isSynthetic() && !edge.isWeak();
+        }
+        return filter;
+    },
+
+    dispose: function()
+    {
+        WebInspector.HeapSnapshot.prototype.dispose.call(this);
+        delete this._flags;
+    },
+
+    _markInvisibleEdges: function()
+    {
+        // Mark hidden edges of global objects as invisible.
+        // FIXME: This is a temporary measure. Normally, we should
+        // really hide all hidden nodes.
+        for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
+            var edge = iter.edge;
+            if (!edge.isShortcut())
+                continue;
+            var node = edge.node();
+            var propNames = {};
+            for (var innerIter = node.edges(); innerIter.hasNext(); innerIter.next()) {
+                var globalObjEdge = innerIter.edge;
+                if (globalObjEdge.isShortcut())
+                    propNames[globalObjEdge._nameOrIndex()] = true;
+            }
+            for (innerIter.rewind(); innerIter.hasNext(); innerIter.next()) {
+                var globalObjEdge = innerIter.edge;
+                if (!globalObjEdge.isShortcut()
+                    && globalObjEdge.node().isHidden()
+                    && globalObjEdge._hasStringName()
+                    && (globalObjEdge._nameOrIndex() in propNames))
+                    this._containmentEdges[globalObjEdge._edges._start + globalObjEdge.edgeIndex + this._edgeTypeOffset] = this._edgeInvisibleType;
+            }
+        }
+    },
+
+    _calculateFlags: function()
+    {
+        this._flags = new Uint32Array(this.nodeCount);
+        this._markDetachedDOMTreeNodes();
+        this._markQueriableHeapObjects();
+        this._markPageOwnedNodes();
+    },
+
+    distanceForUserRoot: function(node)
+    {
+        if (node.isWindow())
+            return 1;
+        if (node.isDocumentDOMTreesRoot())
+            return 0;
+        return -1;
+    },
+
+    userObjectsMapAndFlag: function()
+    {
+        return {
+            map: this._flags,
+            flag: this._nodeFlags.pageObject
+        };
+    },
+
+    _flagsOfNode: function(node)
+    {
+        return this._flags[node.nodeIndex / this._nodeFieldCount];
+    },
+
+    _markDetachedDOMTreeNodes: function()
+    {
+        var flag = this._nodeFlags.detachedDOMTreeNode;
+        var detachedDOMTreesRoot;
+        for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
+            var node = iter.edge.node();
+            if (node.name() === "(Detached DOM trees)") {
+                detachedDOMTreesRoot = node;
+                break;
+            }
+        }
+
+        if (!detachedDOMTreesRoot)
+            return;
+
+        var detachedDOMTreeRE = /^Detached DOM tree/;
+        for (var iter = detachedDOMTreesRoot.edges(); iter.hasNext(); iter.next()) {
+            var node = iter.edge.node();
+            if (detachedDOMTreeRE.test(node.className())) {
+                for (var edgesIter = node.edges(); edgesIter.hasNext(); edgesIter.next())
+                    this._flags[edgesIter.edge.node().nodeIndex / this._nodeFieldCount] |= flag;
+            }
+        }
+    },
+
+    _markQueriableHeapObjects: function()
+    {
+        // Allow runtime properties query for objects accessible from Window objects
+        // via regular properties, and for DOM wrappers. Trying to access random objects
+        // can cause a crash due to insonsistent state of internal properties of wrappers.
+        var flag = this._nodeFlags.canBeQueried;
+        var hiddenEdgeType = this._edgeHiddenType;
+        var internalEdgeType = this._edgeInternalType;
+        var invisibleEdgeType = this._edgeInvisibleType;
+        var weakEdgeType = this._edgeWeakType;
+        var edgeToNodeOffset = this._edgeToNodeOffset;
+        var edgeTypeOffset = this._edgeTypeOffset;
+        var edgeFieldsCount = this._edgeFieldsCount;
+        var containmentEdges = this._containmentEdges;
+        var nodes = this._nodes;
+        var nodeCount = this.nodeCount;
+        var nodeFieldCount = this._nodeFieldCount;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
+
+        var flags = this._flags;
+        var list = [];
+
+        for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
+            if (iter.edge.node().isWindow())
+                list.push(iter.edge.node().nodeIndex / nodeFieldCount);
+        }
+
+        while (list.length) {
+            var nodeOrdinal = list.pop();
+            if (flags[nodeOrdinal] & flag)
+                continue;
+            flags[nodeOrdinal] |= flag;
+            var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+            var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
+            for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
+                var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+                var childNodeOrdinal = childNodeIndex / nodeFieldCount;
+                if (flags[childNodeOrdinal] & flag)
+                    continue;
+                var type = containmentEdges[edgeIndex + edgeTypeOffset];
+                if (type === hiddenEdgeType || type === invisibleEdgeType || type === internalEdgeType || type === weakEdgeType)
+                    continue;
+                list.push(childNodeOrdinal);
+            }
+        }
+    },
+
+    _markPageOwnedNodes: function()
+    {
+        var edgeShortcutType = this._edgeShortcutType;
+        var edgeElementType = this._edgeElementType;
+        var edgeToNodeOffset = this._edgeToNodeOffset;
+        var edgeTypeOffset = this._edgeTypeOffset;
+        var edgeFieldsCount = this._edgeFieldsCount;
+        var edgeWeakType = this._edgeWeakType;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
+        var containmentEdges = this._containmentEdges;
+        var containmentEdgesLength = containmentEdges.length;
+        var nodes = this._nodes;
+        var nodeFieldCount = this._nodeFieldCount;
+        var nodesCount = this.nodeCount;
+
+        var flags = this._flags;
+        var flag = this._nodeFlags.pageObject;
+        var visitedMarker = this._nodeFlags.visitedMarker;
+        var visitedMarkerMask = this._nodeFlags.visitedMarkerMask;
+        var markerAndFlag = visitedMarker | flag;
+
+        var nodesToVisit = new Uint32Array(nodesCount);
+        var nodesToVisitLength = 0;
+
+        var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+        var node = this.rootNode();
+        for (var edgeIndex = firstEdgeIndexes[rootNodeOrdinal], endEdgeIndex = firstEdgeIndexes[rootNodeOrdinal + 1];
+             edgeIndex < endEdgeIndex;
+             edgeIndex += edgeFieldsCount) {
+            var edgeType = containmentEdges[edgeIndex + edgeTypeOffset];
+            var nodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+            if (edgeType === edgeElementType) {
+                node.nodeIndex = nodeIndex;
+                if (!node.isDocumentDOMTreesRoot())
+                    continue;
+            } else if (edgeType !== edgeShortcutType)
+                continue;
+            var nodeOrdinal = nodeIndex / nodeFieldCount;
+            nodesToVisit[nodesToVisitLength++] = nodeOrdinal;
+            flags[nodeOrdinal] |= visitedMarker;
+        }
+
+        while (nodesToVisitLength) {
+            var nodeOrdinal = nodesToVisit[--nodesToVisitLength];
+            flags[nodeOrdinal] |= flag;
+            flags[nodeOrdinal] &= visitedMarkerMask;
+            var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+            var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
+            for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
+                var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
+                var childNodeOrdinal = childNodeIndex / nodeFieldCount;
+                if (flags[childNodeOrdinal] & markerAndFlag)
+                    continue;
+                var type = containmentEdges[edgeIndex + edgeTypeOffset];
+                if (type === edgeWeakType)
+                    continue;
+                nodesToVisit[nodesToVisitLength++] = childNodeOrdinal;
+                flags[childNodeOrdinal] |= visitedMarker;
+            }
+        }
+    },
+
+    __proto__: WebInspector.HeapSnapshot.prototype
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotNode}
+ * @param {WebInspector.JSHeapSnapshot} snapshot
+ * @param {number=} nodeIndex
+ */
+WebInspector.JSHeapSnapshotNode = function(snapshot, nodeIndex)
+{
+    WebInspector.HeapSnapshotNode.call(this, snapshot, nodeIndex)
+}
+
+WebInspector.JSHeapSnapshotNode.prototype = {
+    canBeQueried: function()
+    {
+        var flags = this._snapshot._flagsOfNode(this);
+        return !!(flags & this._snapshot._nodeFlags.canBeQueried);
+    },
+
+    isUserObject: function()
+    {
+        var flags = this._snapshot._flagsOfNode(this);
+        return !!(flags & this._snapshot._nodeFlags.pageObject);
+    },
+
+    className: function()
+    {
+        var type = this.type();
+        switch (type) {
+        case "hidden":
+            return "(system)";
+        case "object":
+        case "native":
+            return this.name();
+        case "code":
+            return "(compiled code)";
+        default:
+            return "(" + type + ")";
+        }
+    },
+
+    classIndex: function()
+    {
+        var snapshot = this._snapshot;
+        var nodes = snapshot._nodes;
+        var type = nodes[this.nodeIndex + snapshot._nodeTypeOffset];;
+        if (type === snapshot._nodeObjectType || type === snapshot._nodeNativeType)
+            return nodes[this.nodeIndex + snapshot._nodeNameOffset];
+        return -1 - type;
+    },
+
+    id: function()
+    {
+        var snapshot = this._snapshot;
+        return snapshot._nodes[this.nodeIndex + snapshot._nodeIdOffset];
+    },
+
+    isHidden: function()
+    {
+        return this._type() === this._snapshot._nodeHiddenType;
+    },
+
+    isSynthetic: function()
+    {
+        return this._type() === this._snapshot._nodeSyntheticType;
+    },
+
+    isWindow: function()
+    {
+        const windowRE = /^Window/;
+        return windowRE.test(this.name());
+    },
+
+    isDocumentDOMTreesRoot: function()
+    {
+        return this.isSynthetic() && this.name() === "(Document DOM trees)";
+    },
+
+    serialize: function()
+    {
+        var result = WebInspector.HeapSnapshotNode.prototype.serialize.call(this);
+        var flags = this._snapshot._flagsOfNode(this);
+        if (flags & this._snapshot._nodeFlags.canBeQueried)
+            result.canBeQueried = true;
+        if (flags & this._snapshot._nodeFlags.detachedDOMTreeNode)
+            result.detachedDOMTreeNode = true;
+        return result;
+    },
+
+    __proto__: WebInspector.HeapSnapshotNode.prototype
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotEdge}
+ * @param {WebInspector.JSHeapSnapshot} snapshot
+ * @param {Array.<number>} edges
+ * @param {number=} edgeIndex
+ */
+WebInspector.JSHeapSnapshotEdge = function(snapshot, edges, edgeIndex)
+{
+    WebInspector.HeapSnapshotEdge.call(this, snapshot, edges, edgeIndex);
+}
+
+WebInspector.JSHeapSnapshotEdge.prototype = {
+    clone: function()
+    {
+        return new WebInspector.JSHeapSnapshotEdge(this._snapshot, this._edges, this.edgeIndex);
+    },
+
+    hasStringName: function()
+    {
+        if (!this.isShortcut())
+            return this._hasStringName();
+        return isNaN(parseInt(this._name(), 10));
+    },
+
+    isElement: function()
+    {
+        return this._type() === this._snapshot._edgeElementType;
+    },
+
+    isHidden: function()
+    {
+        return this._type() === this._snapshot._edgeHiddenType;
+    },
+
+    isWeak: function()
+    {
+        return this._type() === this._snapshot._edgeWeakType;
+    },
+
+    isInternal: function()
+    {
+        return this._type() === this._snapshot._edgeInternalType;
+    },
+
+    isInvisible: function()
+    {
+        return this._type() === this._snapshot._edgeInvisibleType;
+    },
+
+    isShortcut: function()
+    {
+        return this._type() === this._snapshot._edgeShortcutType;
+    },
+
+    name: function()
+    {
+        if (!this.isShortcut())
+            return this._name();
+        var numName = parseInt(this._name(), 10);
+        return isNaN(numName) ? this._name() : numName;
+    },
+
+    toString: function()
+    {
+        var name = this.name();
+        switch (this.type()) {
+        case "context": return "->" + name;
+        case "element": return "[" + name + "]";
+        case "weak": return "[[" + name + "]]";
+        case "property":
+            return name.indexOf(" ") === -1 ? "." + name : "[\"" + name + "\"]";
+        case "shortcut":
+            if (typeof name === "string")
+                return name.indexOf(" ") === -1 ? "." + name : "[\"" + name + "\"]";
+            else
+                return "[" + name + "]";
+        case "internal":
+        case "hidden":
+        case "invisible":
+            return "{" + name + "}";
+        };
+        return "?" + name + "?";
+    },
+
+    _hasStringName: function()
+    {
+        return !this.isElement() && !this.isHidden() && !this.isWeak();
+    },
+
+    _name: function()
+    {
+        return this._hasStringName() ? this._snapshot._strings[this._nameOrIndex()] : this._nameOrIndex();
+    },
+
+    _nameOrIndex: function()
+    {
+        return this._edges.item(this.edgeIndex + this._snapshot._edgeNameOffset);
+    },
+
+    _type: function()
+    {
+        return this._edges.item(this.edgeIndex + this._snapshot._edgeTypeOffset);
+    },
+
+    __proto__: WebInspector.HeapSnapshotEdge.prototype
+};
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotRetainerEdge}
+ * @param {WebInspector.JSHeapSnapshot} snapshot
+ */
+WebInspector.JSHeapSnapshotRetainerEdge = function(snapshot, retainedNodeIndex, retainerIndex)
+{
+    WebInspector.HeapSnapshotRetainerEdge.call(this, snapshot, retainedNodeIndex, retainerIndex);
+}
+
+WebInspector.JSHeapSnapshotRetainerEdge.prototype = {
+    clone: function()
+    {
+        return new WebInspector.JSHeapSnapshotRetainerEdge(this._snapshot, this._retainedNodeIndex, this.retainerIndex());
+    },
+
+    isHidden: function()
+    {
+        return this._edge().isHidden();
+    },
+
+    isInternal: function()
+    {
+        return this._edge().isInternal();
+    },
+
+    isInvisible: function()
+    {
+        return this._edge().isInvisible();
+    },
+
+    isShortcut: function()
+    {
+        return this._edge().isShortcut();
+    },
+
+    isWeak: function()
+    {
+        return this._edge().isWeak();
+    },
+
+    __proto__: WebInspector.HeapSnapshotRetainerEdge.prototype
+}
+
diff --git a/Source/devtools/front_end/JavaScriptFormatter.js b/Source/devtools/front_end/JavaScriptFormatter.js
new file mode 100644
index 0000000..9536289
--- /dev/null
+++ b/Source/devtools/front_end/JavaScriptFormatter.js
@@ -0,0 +1,915 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+function FormattedContentBuilder(content, mapping, originalOffset, formattedOffset, indentString)
+{
+    this._originalContent = content;
+    this._originalOffset = originalOffset;
+    this._lastOriginalPosition = 0;
+
+    this._formattedContent = [];
+    this._formattedContentLength = 0;
+    this._formattedOffset = formattedOffset;
+    this._lastFormattedPosition = 0;
+
+    this._mapping = mapping;
+
+    this._lineNumber = 0;
+    this._nestingLevel = 0;
+    this._indentString = indentString;
+    this._cachedIndents = {};
+}
+
+FormattedContentBuilder.prototype = {
+    addToken: function(token)
+    {
+        for (var i = 0; i < token.comments_before.length; ++i)
+            this._addComment(token.comments_before[i]);
+
+        while (this._lineNumber < token.line) {
+            this._addText("\n");
+            this._addIndent();
+            this._needNewLine = false;
+            this._lineNumber += 1;
+        }
+
+        if (this._needNewLine) {
+            this._addText("\n");
+            this._addIndent();
+            this._needNewLine = false;
+        }
+
+        this._addMappingIfNeeded(token.pos);
+        this._addText(this._originalContent.substring(token.pos, token.endPos));
+        this._lineNumber = token.endLine;
+    },
+
+    addSpace: function()
+    {
+        this._addText(" ");
+    },
+
+    addNewLine: function()
+    {
+        this._needNewLine = true;
+    },
+
+    increaseNestingLevel: function()
+    {
+        this._nestingLevel += 1;
+    },
+
+    decreaseNestingLevel: function()
+    {
+        this._nestingLevel -= 1;
+    },
+
+    content: function()
+    {
+        return this._formattedContent.join("");
+    },
+
+    mapping: function()
+    {
+        return { original: this._originalPositions, formatted: this._formattedPositions };
+    },
+
+    _addIndent: function()
+    {
+        if (this._cachedIndents[this._nestingLevel]) {
+            this._addText(this._cachedIndents[this._nestingLevel]);
+            return;
+        }
+
+        var fullIndent = "";
+        for (var i = 0; i < this._nestingLevel; ++i)
+            fullIndent += this._indentString;
+        this._addText(fullIndent);
+
+        // Cache a maximum of 20 nesting level indents.
+        if (this._nestingLevel <= 20)
+            this._cachedIndents[this._nestingLevel] = fullIndent;
+    },
+
+    _addComment: function(comment)
+    {
+        if (this._lineNumber < comment.line) {
+            for (var j = this._lineNumber; j < comment.line; ++j)
+                this._addText("\n");
+            this._lineNumber = comment.line;
+            this._needNewLine = false;
+            this._addIndent();
+        } else
+            this.addSpace();
+
+        this._addMappingIfNeeded(comment.pos);
+        if (comment.type === "comment1")
+            this._addText("//");
+        else
+            this._addText("/*");
+
+        this._addText(comment.value);
+
+        if (comment.type !== "comment1") {
+            this._addText("*/");
+            var position;
+            while ((position = comment.value.indexOf("\n", position + 1)) !== -1)
+                this._lineNumber += 1;
+        }
+    },
+
+    _addText: function(text)
+    {
+        this._formattedContent.push(text);
+        this._formattedContentLength += text.length;
+    },
+
+    _addMappingIfNeeded: function(originalPosition)
+    {
+        if (originalPosition - this._lastOriginalPosition === this._formattedContentLength - this._lastFormattedPosition)
+            return;
+        this._mapping.original.push(this._originalOffset + originalPosition);
+        this._lastOriginalPosition = originalPosition;
+        this._mapping.formatted.push(this._formattedOffset + this._formattedContentLength);
+        this._lastFormattedPosition = this._formattedContentLength;
+    }
+}
+
+var tokens = [
+    ["EOS"],
+    ["LPAREN", "("], ["RPAREN", ")"], ["LBRACK", "["], ["RBRACK", "]"], ["LBRACE", "{"], ["RBRACE", "}"], ["COLON", ":"], ["SEMICOLON", ";"], ["PERIOD", "."], ["CONDITIONAL", "?"],
+    ["INC", "++"], ["DEC", "--"],
+    ["ASSIGN", "="], ["ASSIGN_BIT_OR", "|="], ["ASSIGN_BIT_XOR", "^="], ["ASSIGN_BIT_AND", "&="], ["ASSIGN_SHL", "<<="], ["ASSIGN_SAR", ">>="], ["ASSIGN_SHR", ">>>="],
+    ["ASSIGN_ADD", "+="], ["ASSIGN_SUB", "-="], ["ASSIGN_MUL", "*="], ["ASSIGN_DIV", "/="], ["ASSIGN_MOD", "%="],
+    ["COMMA", ","], ["OR", "||"], ["AND", "&&"], ["BIT_OR", "|"], ["BIT_XOR", "^"], ["BIT_AND", "&"], ["SHL", "<<"], ["SAR", ">>"], ["SHR", ">>>"],
+    ["ADD", "+"], ["SUB", "-"], ["MUL", "*"], ["DIV", "/"], ["MOD", "%"],
+    ["EQ", "=="], ["NE", "!="], ["EQ_STRICT", "==="], ["NE_STRICT", "!=="], ["LT", "<"], ["GT", ">"], ["LTE", "<="], ["GTE", ">="],
+    ["INSTANCEOF", "instanceof"], ["IN", "in"], ["NOT", "!"], ["BIT_NOT", "~"], ["DELETE", "delete"], ["TYPEOF", "typeof"], ["VOID", "void"],
+    ["BREAK", "break"], ["CASE", "case"], ["CATCH", "catch"], ["CONTINUE", "continue"], ["DEBUGGER", "debugger"], ["DEFAULT", "default"], ["DO", "do"], ["ELSE", "else"], ["FINALLY", "finally"],
+    ["FOR", "for"], ["FUNCTION", "function"], ["IF", "if"], ["NEW", "new"], ["RETURN", "return"], ["SWITCH", "switch"], ["THIS", "this"], ["THROW", "throw"], ["TRY", "try"], ["VAR", "var"],
+    ["WHILE", "while"], ["WITH", "with"], ["NULL_LITERAL", "null"], ["TRUE_LITERAL", "true"], ["FALSE_LITERAL", "false"], ["NUMBER"], ["STRING"], ["IDENTIFIER"], ["CONST", "const"]
+];
+
+var Tokens = {};
+for (var i = 0; i < tokens.length; ++i)
+    Tokens[tokens[i][0]] = i;
+
+var TokensByValue = {};
+for (var i = 0; i < tokens.length; ++i) {
+    if (tokens[i][1])
+        TokensByValue[tokens[i][1]] = i;
+}
+
+var TokensByType = {
+    "eof": Tokens.EOS,
+    "name": Tokens.IDENTIFIER,
+    "num": Tokens.NUMBER,
+    "regexp": Tokens.DIV,
+    "string": Tokens.STRING
+};
+
+function Tokenizer(content)
+{
+    this._readNextToken = parse.tokenizer(content);
+    this._state = this._readNextToken.context();
+}
+
+Tokenizer.prototype = {
+    content: function()
+    {
+        return this._state.text;
+    },
+
+    next: function(forceRegexp)
+    {
+        var uglifyToken = this._readNextToken(forceRegexp);
+        uglifyToken.endPos = this._state.pos;
+        uglifyToken.endLine = this._state.line;
+        uglifyToken.token = this._convertUglifyToken(uglifyToken);
+        return uglifyToken;
+    },
+
+    _convertUglifyToken: function(uglifyToken)
+    {
+        var token = TokensByType[uglifyToken.type];
+        if (typeof token === "number")
+            return token;
+        token = TokensByValue[uglifyToken.value];
+        if (typeof token === "number")
+            return token;
+        throw "Unknown token type " + uglifyToken.type;
+    }
+}
+
+function JavaScriptFormatter(tokenizer, builder)
+{
+    this._tokenizer = tokenizer;
+    this._builder = builder;
+    this._token = null;
+    this._nextToken = this._tokenizer.next();
+}
+
+JavaScriptFormatter.prototype = {
+    format: function()
+    {
+        this._parseSourceElements(Tokens.EOS);
+        this._consume(Tokens.EOS);
+    },
+
+    _peek: function()
+    {
+        return this._nextToken.token;
+    },
+
+    _next: function()
+    {
+        if (this._token && this._token.token === Tokens.EOS)
+            throw "Unexpected EOS token";
+
+        this._builder.addToken(this._nextToken);
+        this._token = this._nextToken;
+        this._nextToken = this._tokenizer.next(this._forceRegexp);
+        this._forceRegexp = false;
+        return this._token.token;
+    },
+
+    _consume: function(token)
+    {
+        var next = this._next();
+        if (next !== token)
+            throw "Unexpected token in consume: expected " + token + ", actual " + next;
+    },
+
+    _expect: function(token)
+    {
+        var next = this._next();
+        if (next !== token)
+            throw "Unexpected token: expected " + token + ", actual " + next;
+    },
+
+    _expectSemicolon: function()
+    {
+        if (this._peek() === Tokens.SEMICOLON)
+            this._consume(Tokens.SEMICOLON);
+    },
+
+    _hasLineTerminatorBeforeNext: function()
+    {
+        return this._nextToken.nlb;
+    },
+
+    _parseSourceElements: function(endToken)
+    {
+        while (this._peek() !== endToken) {
+            this._parseStatement();
+            this._builder.addNewLine();
+        }
+    },
+
+    _parseStatementOrBlock: function()
+    {
+        if (this._peek() === Tokens.LBRACE) {
+            this._builder.addSpace();
+            this._parseBlock();
+            return true;
+        }
+
+        this._builder.addNewLine();
+        this._builder.increaseNestingLevel();
+        this._parseStatement();
+        this._builder.decreaseNestingLevel();
+    },
+
+    _parseStatement: function()
+    {
+        switch (this._peek()) {
+        case Tokens.LBRACE:
+            return this._parseBlock();
+        case Tokens.CONST:
+        case Tokens.VAR:
+            return this._parseVariableStatement();
+        case Tokens.SEMICOLON:
+            return this._next();
+        case Tokens.IF:
+            return this._parseIfStatement();
+        case Tokens.DO:
+            return this._parseDoWhileStatement();
+        case Tokens.WHILE:
+            return this._parseWhileStatement();
+        case Tokens.FOR:
+            return this._parseForStatement();
+        case Tokens.CONTINUE:
+            return this._parseContinueStatement();
+        case Tokens.BREAK:
+            return this._parseBreakStatement();
+        case Tokens.RETURN:
+            return this._parseReturnStatement();
+        case Tokens.WITH:
+            return this._parseWithStatement();
+        case Tokens.SWITCH:
+            return this._parseSwitchStatement();
+        case Tokens.THROW:
+            return this._parseThrowStatement();
+        case Tokens.TRY:
+            return this._parseTryStatement();
+        case Tokens.FUNCTION:
+            return this._parseFunctionDeclaration();
+        case Tokens.DEBUGGER:
+            return this._parseDebuggerStatement();
+        default:
+            return this._parseExpressionOrLabelledStatement();
+        }
+    },
+
+    _parseFunctionDeclaration: function()
+    {
+        this._expect(Tokens.FUNCTION);
+        this._builder.addSpace();
+        this._expect(Tokens.IDENTIFIER);
+        this._parseFunctionLiteral()
+    },
+
+    _parseBlock: function()
+    {
+        this._expect(Tokens.LBRACE);
+        this._builder.addNewLine();
+        this._builder.increaseNestingLevel();
+        while (this._peek() !== Tokens.RBRACE) {
+            this._parseStatement();
+            this._builder.addNewLine();
+        }
+        this._builder.decreaseNestingLevel();
+        this._expect(Tokens.RBRACE);
+    },
+
+    _parseVariableStatement: function()
+    {
+        this._parseVariableDeclarations();
+        this._expectSemicolon();
+    },
+
+    _parseVariableDeclarations: function()
+    {
+        if (this._peek() === Tokens.VAR)
+            this._consume(Tokens.VAR);
+        else
+            this._consume(Tokens.CONST)
+        this._builder.addSpace();
+
+        var isFirstVariable = true;
+        do {
+            if (!isFirstVariable) {
+                this._consume(Tokens.COMMA);
+                this._builder.addSpace();
+            }
+            isFirstVariable = false;
+            this._expect(Tokens.IDENTIFIER);
+            if (this._peek() === Tokens.ASSIGN) {
+                this._builder.addSpace();
+                this._consume(Tokens.ASSIGN);
+                this._builder.addSpace();
+                this._parseAssignmentExpression();
+            }
+        } while (this._peek() === Tokens.COMMA);
+    },
+
+    _parseExpressionOrLabelledStatement: function()
+    {
+        this._parseExpression();
+        if (this._peek() === Tokens.COLON) {
+            this._expect(Tokens.COLON);
+            this._builder.addSpace();
+            this._parseStatement();
+        }
+        this._expectSemicolon();
+    },
+
+    _parseIfStatement: function()
+    {
+        this._expect(Tokens.IF);
+        this._builder.addSpace();
+        this._expect(Tokens.LPAREN);
+        this._parseExpression();
+        this._expect(Tokens.RPAREN);
+
+        var isBlock = this._parseStatementOrBlock();
+        if (this._peek() === Tokens.ELSE) {
+            if (isBlock)
+                this._builder.addSpace();
+            else
+                this._builder.addNewLine();
+            this._next();
+
+            if (this._peek() === Tokens.IF) {
+                this._builder.addSpace();
+                this._parseStatement();
+            } else
+                this._parseStatementOrBlock();
+        }
+    },
+
+    _parseContinueStatement: function()
+    {
+        this._expect(Tokens.CONTINUE);
+        var token = this._peek();
+        if (!this._hasLineTerminatorBeforeNext() && token !== Tokens.SEMICOLON && token !== Tokens.RBRACE && token !== Tokens.EOS) {
+            this._builder.addSpace();
+            this._expect(Tokens.IDENTIFIER);
+        }
+        this._expectSemicolon();
+    },
+
+    _parseBreakStatement: function()
+    {
+        this._expect(Tokens.BREAK);
+        var token = this._peek();
+        if (!this._hasLineTerminatorBeforeNext() && token !== Tokens.SEMICOLON && token !== Tokens.RBRACE && token !== Tokens.EOS) {
+            this._builder.addSpace();
+            this._expect(Tokens.IDENTIFIER);
+        }
+        this._expectSemicolon();
+    },
+
+    _parseReturnStatement: function()
+    {
+        this._expect(Tokens.RETURN);
+        var token = this._peek();
+        if (!this._hasLineTerminatorBeforeNext() && token !== Tokens.SEMICOLON && token !== Tokens.RBRACE && token !== Tokens.EOS) {
+            this._builder.addSpace();
+            this._parseExpression();
+        }
+        this._expectSemicolon();
+    },
+
+    _parseWithStatement: function()
+    {
+        this._expect(Tokens.WITH);
+        this._builder.addSpace();
+        this._expect(Tokens.LPAREN);
+        this._parseExpression();
+        this._expect(Tokens.RPAREN);
+        this._parseStatementOrBlock();
+    },
+
+    _parseCaseClause: function()
+    {
+        if (this._peek() === Tokens.CASE) {
+            this._expect(Tokens.CASE);
+            this._builder.addSpace();
+            this._parseExpression();
+        } else
+            this._expect(Tokens.DEFAULT);
+        this._expect(Tokens.COLON);
+        this._builder.addNewLine();
+
+        this._builder.increaseNestingLevel();
+        while (this._peek() !== Tokens.CASE && this._peek() !== Tokens.DEFAULT && this._peek() !== Tokens.RBRACE) {
+            this._parseStatement();
+            this._builder.addNewLine();
+        }
+        this._builder.decreaseNestingLevel();
+    },
+
+    _parseSwitchStatement: function()
+    {
+        this._expect(Tokens.SWITCH);
+        this._builder.addSpace();
+        this._expect(Tokens.LPAREN);
+        this._parseExpression();
+        this._expect(Tokens.RPAREN);
+        this._builder.addSpace();
+
+        this._expect(Tokens.LBRACE);
+        this._builder.addNewLine();
+        this._builder.increaseNestingLevel();
+        while (this._peek() !== Tokens.RBRACE)
+            this._parseCaseClause();
+        this._builder.decreaseNestingLevel();
+        this._expect(Tokens.RBRACE);
+    },
+
+    _parseThrowStatement: function()
+    {
+        this._expect(Tokens.THROW);
+        this._builder.addSpace();
+        this._parseExpression();
+        this._expectSemicolon();
+    },
+
+    _parseTryStatement: function()
+    {
+        this._expect(Tokens.TRY);
+        this._builder.addSpace();
+        this._parseBlock();
+
+        var token = this._peek();
+        if (token === Tokens.CATCH) {
+            this._builder.addSpace();
+            this._consume(Tokens.CATCH);
+            this._builder.addSpace();
+            this._expect(Tokens.LPAREN);
+            this._expect(Tokens.IDENTIFIER);
+            this._expect(Tokens.RPAREN);
+            this._builder.addSpace();
+            this._parseBlock();
+            token = this._peek();
+        }
+
+        if (token === Tokens.FINALLY) {
+            this._consume(Tokens.FINALLY);
+            this._builder.addSpace();
+            this._parseBlock();
+        }
+    },
+
+    _parseDoWhileStatement: function()
+    {
+        this._expect(Tokens.DO);
+        var isBlock = this._parseStatementOrBlock();
+        if (isBlock)
+            this._builder.addSpace();
+        else
+            this._builder.addNewLine();
+        this._expect(Tokens.WHILE);
+        this._builder.addSpace();
+        this._expect(Tokens.LPAREN);
+        this._parseExpression();
+        this._expect(Tokens.RPAREN);
+        this._expectSemicolon();
+    },
+
+    _parseWhileStatement: function()
+    {
+        this._expect(Tokens.WHILE);
+        this._builder.addSpace();
+        this._expect(Tokens.LPAREN);
+        this._parseExpression();
+        this._expect(Tokens.RPAREN);
+        this._parseStatementOrBlock();
+    },
+
+    _parseForStatement: function()
+    {
+        this._expect(Tokens.FOR);
+        this._builder.addSpace();
+        this._expect(Tokens.LPAREN);
+        if (this._peek() !== Tokens.SEMICOLON) {
+            if (this._peek() === Tokens.VAR || this._peek() === Tokens.CONST) {
+                this._parseVariableDeclarations();
+                if (this._peek() === Tokens.IN) {
+                    this._builder.addSpace();
+                    this._consume(Tokens.IN);
+                    this._builder.addSpace();
+                    this._parseExpression();
+                }
+            } else
+                this._parseExpression();
+        }
+
+        if (this._peek() !== Tokens.RPAREN) {
+            this._expect(Tokens.SEMICOLON);
+            this._builder.addSpace();
+            if (this._peek() !== Tokens.SEMICOLON)
+                this._parseExpression();
+            this._expect(Tokens.SEMICOLON);
+            this._builder.addSpace();
+            if (this._peek() !== Tokens.RPAREN)
+                this._parseExpression();
+        }
+        this._expect(Tokens.RPAREN);
+
+        this._parseStatementOrBlock();
+    },
+
+    _parseExpression: function()
+    {
+        this._parseAssignmentExpression();
+        while (this._peek() === Tokens.COMMA) {
+            this._expect(Tokens.COMMA);
+            this._builder.addSpace();
+            this._parseAssignmentExpression();
+        }
+    },
+
+    _parseAssignmentExpression: function()
+    {
+        this._parseConditionalExpression();
+        var token = this._peek();
+        if (Tokens.ASSIGN <= token && token <= Tokens.ASSIGN_MOD) {
+            this._builder.addSpace();
+            this._next();
+            this._builder.addSpace();
+            this._parseAssignmentExpression();
+        }
+    },
+
+    _parseConditionalExpression: function()
+    {
+        this._parseBinaryExpression();
+        if (this._peek() === Tokens.CONDITIONAL) {
+            this._builder.addSpace();
+            this._consume(Tokens.CONDITIONAL);
+            this._builder.addSpace();
+            this._parseAssignmentExpression();
+            this._builder.addSpace();
+            this._expect(Tokens.COLON);
+            this._builder.addSpace();
+            this._parseAssignmentExpression();
+        }
+    },
+
+    _parseBinaryExpression: function()
+    {
+        this._parseUnaryExpression();
+        var token = this._peek();
+        while (Tokens.OR <= token && token <= Tokens.IN) {
+            this._builder.addSpace();
+            this._next();
+            this._builder.addSpace();
+            this._parseBinaryExpression();
+            token = this._peek();
+        }
+    },
+
+    _parseUnaryExpression: function()
+    {
+        var token = this._peek();
+        if ((Tokens.NOT <= token && token <= Tokens.VOID) || token === Tokens.ADD || token === Tokens.SUB || token ===  Tokens.INC || token === Tokens.DEC) {
+            this._next();
+            if (token === Tokens.DELETE || token === Tokens.TYPEOF || token === Tokens.VOID)
+                this._builder.addSpace();
+            this._parseUnaryExpression();
+        } else
+            return this._parsePostfixExpression();
+    },
+
+    _parsePostfixExpression: function()
+    {
+        this._parseLeftHandSideExpression();
+        var token = this._peek();
+        if (!this._hasLineTerminatorBeforeNext() && (token === Tokens.INC || token === Tokens.DEC))
+            this._next();
+    },
+
+    _parseLeftHandSideExpression: function()
+    {
+        if (this._peek() === Tokens.NEW)
+            this._parseNewExpression();
+        else
+            this._parseMemberExpression();
+
+        while (true) {
+            switch (this._peek()) {
+            case Tokens.LBRACK:
+                this._consume(Tokens.LBRACK);
+                this._parseExpression();
+                this._expect(Tokens.RBRACK);
+                break;
+
+            case Tokens.LPAREN:
+                this._parseArguments();
+                break;
+
+            case Tokens.PERIOD:
+                this._consume(Tokens.PERIOD);
+                this._expect(Tokens.IDENTIFIER);
+                break;
+
+            default:
+                return;
+            }
+        }
+    },
+
+    _parseNewExpression: function()
+    {
+        this._expect(Tokens.NEW);
+        this._builder.addSpace();
+        if (this._peek() === Tokens.NEW)
+            this._parseNewExpression();
+        else
+            this._parseMemberExpression();
+    },
+
+    _parseMemberExpression: function()
+    {
+        if (this._peek() === Tokens.FUNCTION) {
+            this._expect(Tokens.FUNCTION);
+            if (this._peek() === Tokens.IDENTIFIER) {
+                this._builder.addSpace();
+                this._expect(Tokens.IDENTIFIER);
+            }
+            this._parseFunctionLiteral();
+        } else
+            this._parsePrimaryExpression();
+
+        while (true) {
+            switch (this._peek()) {
+            case Tokens.LBRACK:
+                this._consume(Tokens.LBRACK);
+                this._parseExpression();
+                this._expect(Tokens.RBRACK);
+                break;
+
+            case Tokens.PERIOD:
+                this._consume(Tokens.PERIOD);
+                this._expect(Tokens.IDENTIFIER);
+                break;
+
+            case Tokens.LPAREN:
+                this._parseArguments();
+                break;
+
+            default:
+                return;
+            }
+        }
+    },
+
+    _parseDebuggerStatement: function()
+    {
+        this._expect(Tokens.DEBUGGER);
+        this._expectSemicolon();
+    },
+
+    _parsePrimaryExpression: function()
+    {
+        switch (this._peek()) {
+        case Tokens.THIS:
+            return this._consume(Tokens.THIS);
+        case Tokens.NULL_LITERAL:
+            return this._consume(Tokens.NULL_LITERAL);
+        case Tokens.TRUE_LITERAL:
+            return this._consume(Tokens.TRUE_LITERAL);
+        case Tokens.FALSE_LITERAL:
+            return this._consume(Tokens.FALSE_LITERAL);
+        case Tokens.IDENTIFIER:
+            return this._consume(Tokens.IDENTIFIER);
+        case Tokens.NUMBER:
+            return this._consume(Tokens.NUMBER);
+        case Tokens.STRING:
+            return this._consume(Tokens.STRING);
+        case Tokens.ASSIGN_DIV:
+            return this._parseRegExpLiteral();
+        case Tokens.DIV:
+            return this._parseRegExpLiteral();
+        case Tokens.LBRACK:
+            return this._parseArrayLiteral();
+        case Tokens.LBRACE:
+            return this._parseObjectLiteral();
+        case Tokens.LPAREN:
+            this._consume(Tokens.LPAREN);
+            this._parseExpression();
+            this._expect(Tokens.RPAREN);
+            return;
+        default:
+            return this._next();
+        }
+    },
+
+    _parseArrayLiteral: function()
+    {
+        this._expect(Tokens.LBRACK);
+        this._builder.increaseNestingLevel();
+        while (this._peek() !== Tokens.RBRACK) {
+            if (this._peek() !== Tokens.COMMA)
+                this._parseAssignmentExpression();
+            if (this._peek() !== Tokens.RBRACK) {
+                this._expect(Tokens.COMMA);
+                this._builder.addSpace();
+            }
+        }
+        this._builder.decreaseNestingLevel();
+        this._expect(Tokens.RBRACK);
+    },
+
+    _parseObjectLiteralGetSet: function()
+    {
+        var token = this._peek();
+        if (token === Tokens.IDENTIFIER || token === Tokens.NUMBER || token === Tokens.STRING ||
+            Tokens.DELETE <= token && token <= Tokens.FALSE_LITERAL ||
+            token === Tokens.INSTANCEOF || token === Tokens.IN || token === Tokens.CONST) {
+            this._next();
+            this._parseFunctionLiteral();
+        }
+    },
+
+    _parseObjectLiteral: function()
+    {
+        this._expect(Tokens.LBRACE);
+        this._builder.increaseNestingLevel();
+        while (this._peek() !== Tokens.RBRACE) {
+            var token = this._peek();
+            switch (token) {
+            case Tokens.IDENTIFIER:
+                this._consume(Tokens.IDENTIFIER);
+                var name = this._token.value;
+                if ((name === "get" || name === "set") && this._peek() !== Tokens.COLON) {
+                    this._builder.addSpace();
+                    this._parseObjectLiteralGetSet();
+                    if (this._peek() !== Tokens.RBRACE) {
+                        this._expect(Tokens.COMMA);
+                    }
+                    continue;
+                }
+                break;
+
+            case Tokens.STRING:
+                this._consume(Tokens.STRING);
+                break;
+
+            case Tokens.NUMBER:
+                this._consume(Tokens.NUMBER);
+                break;
+
+            default:
+                this._next();
+            }
+
+            this._expect(Tokens.COLON);
+            this._builder.addSpace();
+            this._parseAssignmentExpression();
+            if (this._peek() !== Tokens.RBRACE) {
+                this._expect(Tokens.COMMA);
+            }
+        }
+        this._builder.decreaseNestingLevel();
+
+        this._expect(Tokens.RBRACE);
+    },
+
+    _parseRegExpLiteral: function()
+    {
+        if (this._nextToken.type === "regexp")
+            this._next();
+        else {
+            this._forceRegexp = true;
+            this._next();
+        }
+    },
+
+    _parseArguments: function()
+    {
+        this._expect(Tokens.LPAREN);
+        var done = (this._peek() === Tokens.RPAREN);
+        while (!done) {
+            this._parseAssignmentExpression();
+            done = (this._peek() === Tokens.RPAREN);
+            if (!done) {
+                this._expect(Tokens.COMMA);
+                this._builder.addSpace();
+            }
+        }
+        this._expect(Tokens.RPAREN);
+    },
+
+    _parseFunctionLiteral: function()
+    {
+        this._expect(Tokens.LPAREN);
+        var done = (this._peek() === Tokens.RPAREN);
+        while (!done) {
+            this._expect(Tokens.IDENTIFIER);
+            done = (this._peek() === Tokens.RPAREN);
+            if (!done) {
+                this._expect(Tokens.COMMA);
+                this._builder.addSpace();
+            }
+        }
+        this._expect(Tokens.RPAREN);
+        this._builder.addSpace();
+
+        this._expect(Tokens.LBRACE);
+        this._builder.addNewLine();
+        this._builder.increaseNestingLevel();
+        this._parseSourceElements(Tokens.RBRACE);
+        this._builder.decreaseNestingLevel();
+        this._expect(Tokens.RBRACE);
+    }
+}
diff --git a/Source/devtools/front_end/JavaScriptSourceFrame.js b/Source/devtools/front_end/JavaScriptSourceFrame.js
new file mode 100644
index 0000000..fd27de8
--- /dev/null
+++ b/Source/devtools/front_end/JavaScriptSourceFrame.js
@@ -0,0 +1,604 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.UISourceCodeFrame}
+ * @param {WebInspector.ScriptsPanel} scriptsPanel
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ */
+WebInspector.JavaScriptSourceFrame = function(scriptsPanel, uiSourceCode)
+{
+    this._scriptsPanel = scriptsPanel;
+    this._breakpointManager = WebInspector.breakpointManager;
+    this._uiSourceCode = uiSourceCode;
+
+    WebInspector.UISourceCodeFrame.call(this, uiSourceCode);
+    if (uiSourceCode.project().type() === WebInspector.projectTypes.Debugger)
+        this.element.addStyleClass("source-frame-debugger-script");
+
+    this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.textEditor.element,
+            this._getPopoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), this._onHidePopover.bind(this), true);
+
+    this.textEditor.element.addEventListener("keydown", this._onKeyDown.bind(this), true);
+
+    this.textEditor.addEventListener(WebInspector.TextEditor.Events.GutterClick, this._handleGutterClick.bind(this), this);
+
+    this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this);
+    this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this);
+
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageAdded, this._consoleMessageAdded, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, this._consoleMessageRemoved, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessagesCleared, this._consoleMessagesCleared, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+
+    this._updateScriptFile();
+}
+
+WebInspector.JavaScriptSourceFrame.prototype = {
+    // View events
+    wasShown: function()
+    {
+        WebInspector.UISourceCodeFrame.prototype.wasShown.call(this);
+    },
+
+    willHide: function()
+    {
+        WebInspector.UISourceCodeFrame.prototype.willHide.call(this);
+        this._popoverHelper.hidePopover();
+    },
+
+    onUISourceCodeContentChanged: function()
+    {
+        this._removeAllBreakpoints();
+        WebInspector.UISourceCodeFrame.prototype.onUISourceCodeContentChanged.call(this);
+    },
+
+    populateLineGutterContextMenu: function(contextMenu, lineNumber)
+    {
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Continue to here" : "Continue to Here"), this._continueToLine.bind(this, lineNumber));
+
+        var breakpoint = this._breakpointManager.findBreakpoint(this._uiSourceCode, lineNumber);
+        if (!breakpoint) {
+            // This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint.
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add breakpoint" : "Add Breakpoint"), this._setBreakpoint.bind(this, lineNumber, "", true));
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add conditional breakpoint…" : "Add Conditional Breakpoint…"), this._editBreakpointCondition.bind(this, lineNumber));
+        } else {
+            // This row has a breakpoint, we want to show edit and remove breakpoint, and either disable or enable.
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove breakpoint" : "Remove Breakpoint"), breakpoint.remove.bind(breakpoint));
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Edit breakpoint…" : "Edit Breakpoint…"), this._editBreakpointCondition.bind(this, lineNumber, breakpoint));
+            if (breakpoint.enabled())
+                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Disable breakpoint" : "Disable Breakpoint"), breakpoint.setEnabled.bind(breakpoint, false));
+            else
+                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Enable breakpoint" : "Enable Breakpoint"), breakpoint.setEnabled.bind(breakpoint, true));
+        }
+    },
+
+    populateTextAreaContextMenu: function(contextMenu, lineNumber)
+    {
+        var selection = window.getSelection();
+        if (selection.type === "Range" && !selection.isCollapsed) {
+            var addToWatchLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add to watch" : "Add to Watch");
+            contextMenu.appendItem(addToWatchLabel, this._scriptsPanel.addToWatch.bind(this._scriptsPanel, selection.toString()));
+            var evaluateLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Evaluate in console" : "Evaluate in Console");
+            contextMenu.appendItem(evaluateLabel, WebInspector.evaluateInConsole.bind(WebInspector, selection.toString()));
+            contextMenu.appendSeparator();
+        } else if (!this._uiSourceCode.isEditable() && this._uiSourceCode.contentType() === WebInspector.resourceTypes.Script) {
+            function liveEdit(event)
+            {
+                var liveEditUISourceCode = WebInspector.liveEditSupport.uiSourceCodeForLiveEdit(this._uiSourceCode);
+                this._scriptsPanel.showUISourceCode(liveEditUISourceCode, lineNumber)
+            }
+
+            // FIXME: Change condition above to explicitly check that current uiSourceCode is created by default debugger mapping
+            // and move the code adding this menu item to generic context menu provider for UISourceCode.
+            var liveEditLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Live edit" : "Live Edit");
+            contextMenu.appendItem(liveEditLabel, liveEdit.bind(this));
+            contextMenu.appendSeparator();
+        }
+        WebInspector.UISourceCodeFrame.prototype.populateTextAreaContextMenu.call(this, contextMenu, lineNumber);
+    },
+
+    _workingCopyChanged: function(event)
+    {
+        if (this._supportsEnabledBreakpointsWhileEditing() || this._scriptFile)
+            return;
+
+        if (this._uiSourceCode.isDirty())
+            this._muteBreakpointsWhileEditing();
+        else
+            this._restoreBreakpointsAfterEditing();
+    },
+
+    _workingCopyCommitted: function(event)
+    {
+        if (this._supportsEnabledBreakpointsWhileEditing() || this._scriptFile)
+            return;
+        this._restoreBreakpointsAfterEditing();
+    },
+
+    _didMergeToVM: function()
+    {
+        if (this._supportsEnabledBreakpointsWhileEditing())
+            return;
+        this._restoreBreakpointsAfterEditing();
+    },
+
+    _didDivergeFromVM: function()
+    {
+        if (this._supportsEnabledBreakpointsWhileEditing())
+            return;
+        this._muteBreakpointsWhileEditing();
+    },
+
+    _muteBreakpointsWhileEditing: function()
+    {
+        if (this._muted)
+            return;
+        for (var lineNumber = 0; lineNumber < this._textEditor.linesCount; ++lineNumber) {
+            var breakpointDecoration = this._textEditor.getAttribute(lineNumber, "breakpoint");
+            if (!breakpointDecoration)
+                continue;
+            this._removeBreakpointDecoration(lineNumber);
+            this._addBreakpointDecoration(lineNumber, breakpointDecoration.condition, breakpointDecoration.enabled, true);
+        }
+        this._muted = true;
+    },
+
+    _supportsEnabledBreakpointsWhileEditing: function()
+    {
+        return this._uiSourceCode.project().type() === WebInspector.projectTypes.Snippets;
+    },
+
+    _restoreBreakpointsAfterEditing: function()
+    {
+        delete this._muted;
+        var breakpoints = {};
+        // Save and remove muted breakpoint decorations.
+        for (var lineNumber = 0; lineNumber < this._textEditor.linesCount; ++lineNumber) {
+            var breakpointDecoration = this._textEditor.getAttribute(lineNumber, "breakpoint");
+            if (breakpointDecoration) {
+                breakpoints[lineNumber] = breakpointDecoration;
+                this._removeBreakpointDecoration(lineNumber);
+            }
+        }
+
+        // Remove all breakpoints.
+        this._removeAllBreakpoints();
+
+        // Restore all breakpoints from saved decorations.
+        for (var lineNumberString in breakpoints) {
+            var lineNumber = parseInt(lineNumberString, 10);
+            if (isNaN(lineNumber))
+                continue;
+            var breakpointDecoration = breakpoints[lineNumberString];
+            this._setBreakpoint(lineNumber, breakpointDecoration.condition, breakpointDecoration.enabled);
+        }
+    },
+
+    _removeAllBreakpoints: function()
+    {
+        var breakpoints = this._breakpointManager.breakpointsForUISourceCode(this._uiSourceCode);
+        for (var i = 0; i < breakpoints.length; ++i)
+            breakpoints[i].remove();
+    },
+
+    _getPopoverAnchor: function(element, event)
+    {
+        if (!WebInspector.debuggerModel.isPaused())
+            return null;
+        if (window.getSelection().type === "Range")
+            return null;
+
+        var textPosition = this.textEditor.coordinatesToCursorPosition(event.x, event.y);
+        if (!textPosition)
+            return null;
+
+        var token = this.textEditor.tokenAtTextPosition(textPosition.startLine, textPosition.startColumn);
+        if (!token)
+            return null;
+        var lineNumber = textPosition.startLine;
+        var line = this.textEditor.line(lineNumber);
+        var tokenContent = line.substring(token.startColumn, token.endColumn + 1);
+        if (token.type !== "javascript-ident" && (token.type !== "javascript-keyword" || tokenContent !== "this"))
+            return null;
+
+        var leftCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.startColumn);
+        var rightCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.endColumn + 1);
+        var anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height);
+
+        anchorBox.token = token;
+        anchorBox.lineNumber = lineNumber;
+
+        return anchorBox;
+    },
+
+    _resolveObjectForPopover: function(anchorBox, showCallback, objectGroupName)
+    {
+        /**
+         * @param {?RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function showObjectPopover(result, wasThrown)
+        {
+            if (!WebInspector.debuggerModel.isPaused()) {
+                this._popoverHelper.hidePopover();
+                return;
+            }
+            this._popoverAnchorBox = anchorBox;
+            showCallback(WebInspector.RemoteObject.fromPayload(result), wasThrown, this._popoverAnchorBox);
+            // Popover may have been removed by showCallback().
+            if (this._popoverAnchorBox) {
+                var highlightRange = new WebInspector.TextRange(anchorBox.lineNumber, startHighlight, anchorBox.lineNumber, endHighlight);
+                this._popoverAnchorBox._highlightDescriptor = this.textEditor.highlightRange(highlightRange, "source-frame-eval-expression");
+            }
+        }
+
+        if (!WebInspector.debuggerModel.isPaused()) {
+            this._popoverHelper.hidePopover();
+            return;
+        }
+
+        var startHighlight = anchorBox.token.startColumn;
+        var endHighlight = anchorBox.token.endColumn;
+        var line = this.textEditor.line(anchorBox.lineNumber);
+        while (startHighlight > 1 && line.charAt(startHighlight - 1) === '.')
+            startHighlight = this.textEditor.tokenAtTextPosition(anchorBox.lineNumber, startHighlight - 2).startColumn;
+        var evaluationText = line.substring(startHighlight, endHighlight + 1);
+
+        var selectedCallFrame = WebInspector.debuggerModel.selectedCallFrame();
+        selectedCallFrame.evaluate(evaluationText, objectGroupName, false, true, false, false, showObjectPopover.bind(this));
+    },
+
+    _onHidePopover: function()
+    {
+        if (!this._popoverAnchorBox)
+            return;
+        if (this._popoverAnchorBox._highlightDescriptor)
+            this.textEditor.removeHighlight(this._popoverAnchorBox._highlightDescriptor);
+        delete this._popoverAnchorBox;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string} condition
+     * @param {boolean} enabled
+     * @param {boolean} mutedWhileEditing
+     */
+    _addBreakpointDecoration: function(lineNumber, condition, enabled, mutedWhileEditing)
+    {
+        var breakpoint = {
+            condition: condition,
+            enabled: enabled
+        };
+
+        this.textEditor.setAttribute(lineNumber, "breakpoint", breakpoint);
+
+        var disabled = !enabled || mutedWhileEditing;
+        this.textEditor.addBreakpoint(lineNumber, disabled, !!condition);
+    },
+
+    _removeBreakpointDecoration: function(lineNumber)
+    {
+        this.textEditor.removeAttribute(lineNumber, "breakpoint");
+        this.textEditor.removeBreakpoint(lineNumber);
+    },
+
+    _onKeyDown: function(event)
+    {
+        if (event.keyIdentifier === "U+001B") { // Escape key
+            if (this._popoverHelper.isPopoverVisible()) {
+                this._popoverHelper.hidePopover();
+                event.consume();
+            }
+        }
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {WebInspector.BreakpointManager.Breakpoint=} breakpoint
+     */
+    _editBreakpointCondition: function(lineNumber, breakpoint)
+    {
+        this._conditionElement = this._createConditionElement(lineNumber);
+        this.textEditor.addDecoration(lineNumber, this._conditionElement);
+
+        function finishEditing(committed, element, newText)
+        {
+            this.textEditor.removeDecoration(lineNumber, this._conditionElement);
+            delete this._conditionEditorElement;
+            delete this._conditionElement;
+            if (breakpoint)
+                breakpoint.setCondition(newText);
+            else
+                this._setBreakpoint(lineNumber, newText, true);
+        }
+
+        var config = new WebInspector.EditingConfig(finishEditing.bind(this, true), finishEditing.bind(this, false));
+        WebInspector.startEditing(this._conditionEditorElement, config);
+        this._conditionEditorElement.value = breakpoint ? breakpoint.condition() : "";
+        this._conditionEditorElement.select();
+    },
+
+    _createConditionElement: function(lineNumber)
+    {
+        var conditionElement = document.createElement("div");
+        conditionElement.className = "source-frame-breakpoint-condition";
+
+        var labelElement = document.createElement("label");
+        labelElement.className = "source-frame-breakpoint-message";
+        labelElement.htmlFor = "source-frame-breakpoint-condition";
+        labelElement.appendChild(document.createTextNode(WebInspector.UIString("The breakpoint on line %d will stop only if this expression is true:", lineNumber)));
+        conditionElement.appendChild(labelElement);
+
+        var editorElement = document.createElement("input");
+        editorElement.id = "source-frame-breakpoint-condition";
+        editorElement.className = "monospace";
+        editorElement.type = "text";
+        conditionElement.appendChild(editorElement);
+        this._conditionEditorElement = editorElement;
+
+        return conditionElement;
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    setExecutionLine: function(lineNumber)
+    {
+        this._executionLineNumber = lineNumber;
+        if (this.loaded) {
+            this.textEditor.setExecutionLine(lineNumber);
+            this.revealLine(this._executionLineNumber);
+            if (this.canEditSource())
+                this.setSelection(WebInspector.TextRange.createFromLocation(lineNumber, 0));
+        }
+    },
+
+    clearExecutionLine: function()
+    {
+        if (this.loaded && typeof this._executionLineNumber === "number")
+            this.textEditor.clearExecutionLine();
+        delete this._executionLineNumber;
+    },
+
+    _lineNumberAfterEditing: function(lineNumber, oldRange, newRange)
+    {
+        var shiftOffset = lineNumber <= oldRange.startLine ? 0 : newRange.linesCount - oldRange.linesCount;
+
+        // Special case of editing the line itself. We should decide whether the line number should move below or not.
+        if (lineNumber === oldRange.startLine) {
+            var whiteSpacesRegex = /^[\s\xA0]*$/;
+            for (var i = 0; lineNumber + i <= newRange.endLine; ++i) {
+                if (!whiteSpacesRegex.test(this.textEditor.line(lineNumber + i))) {
+                    shiftOffset = i;
+                    break;
+                }
+            }
+        }
+
+        var newLineNumber = Math.max(0, lineNumber + shiftOffset);
+        if (oldRange.startLine < lineNumber && lineNumber < oldRange.endLine)
+            newLineNumber = oldRange.startLine;
+        return newLineNumber;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _shouldIgnoreExternalBreakpointEvents: function()
+    {
+        if (this._supportsEnabledBreakpointsWhileEditing())
+            return false;
+        if (this._muted)
+            return true;
+        return this._scriptFile && (this._scriptFile.isDivergingFromVM() || this._scriptFile.isMergingToVM());
+    },
+
+    _breakpointAdded: function(event)
+    {
+        var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation);
+        if (uiLocation.uiSourceCode !== this._uiSourceCode)
+            return;
+        if (this._shouldIgnoreExternalBreakpointEvents())
+            return;
+
+        var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint);
+        if (this.loaded)
+            this._addBreakpointDecoration(uiLocation.lineNumber, breakpoint.condition(), breakpoint.enabled(), false);
+    },
+
+    _breakpointRemoved: function(event)
+    {
+        var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation);
+        if (uiLocation.uiSourceCode !== this._uiSourceCode)
+            return;
+        if (this._shouldIgnoreExternalBreakpointEvents())
+            return;
+
+        var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint);
+        var remainingBreakpoint = this._breakpointManager.findBreakpoint(this._uiSourceCode, uiLocation.lineNumber);
+        if (!remainingBreakpoint && this.loaded)
+            this._removeBreakpointDecoration(uiLocation.lineNumber);
+    },
+
+    _consoleMessageAdded: function(event)
+    {
+        var message = /** @type {WebInspector.PresentationConsoleMessage} */ (event.data);
+        if (this.loaded)
+            this.addMessageToSource(message.lineNumber, message.originalMessage);
+    },
+
+    _consoleMessageRemoved: function(event)
+    {
+        var message = /** @type {WebInspector.PresentationConsoleMessage} */ (event.data);
+        if (this.loaded)
+            this.removeMessageFromSource(message.lineNumber, message.originalMessage);
+    },
+
+    _consoleMessagesCleared: function(event)
+    {
+        this.clearMessages();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onSourceMappingChanged: function(event)
+    {
+        this._updateScriptFile();
+    },
+
+    _updateScriptFile: function()
+    {
+        if (this._scriptFile) {
+            this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this);
+            this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this);
+            if (this._muted && !this._uiSourceCode.isDirty())
+                this._restoreBreakpointsAfterEditing();
+        }
+        this._scriptFile = this._uiSourceCode.scriptFile();
+        if (this._scriptFile) {
+            this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this);
+            this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this);
+    
+            if (this.loaded)
+                this._scriptFile.checkMapping();
+        }
+    },
+
+    onTextEditorContentLoaded: function()
+    {
+        if (typeof this._executionLineNumber === "number")
+            this.setExecutionLine(this._executionLineNumber);
+
+        var breakpointLocations = this._breakpointManager.breakpointLocationsForUISourceCode(this._uiSourceCode);
+        for (var i = 0; i < breakpointLocations.length; ++i)
+            this._breakpointAdded({data:breakpointLocations[i]});
+
+        var messages = this._uiSourceCode.consoleMessages();
+        for (var i = 0; i < messages.length; ++i) {
+            var message = messages[i];
+            this.addMessageToSource(message.lineNumber, message.originalMessage);
+        }
+        
+        if (this._scriptFile)
+            this._scriptFile.checkMapping();
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _handleGutterClick: function(event)
+    {
+        if (this._muted)
+            return;
+
+        var eventData = /** @type {WebInspector.TextEditor.GutterClickEventData} */ (event.data);
+        var lineNumber = eventData.lineNumber;
+        var eventObject = /** @type {Event} */ (eventData.event);
+
+        if (eventObject.button != 0 || eventObject.altKey || eventObject.ctrlKey || eventObject.metaKey)
+            return;
+
+        this._toggleBreakpoint(lineNumber, eventObject.shiftKey);
+        eventObject.consume(true);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {boolean} onlyDisable
+     */
+    _toggleBreakpoint: function(lineNumber, onlyDisable)
+    {
+        var breakpoint = this._breakpointManager.findBreakpoint(this._uiSourceCode, lineNumber);
+        if (breakpoint) {
+            if (onlyDisable)
+                breakpoint.setEnabled(!breakpoint.enabled());
+            else
+                breakpoint.remove();
+        } else
+            this._setBreakpoint(lineNumber, "", true);
+    },
+
+    toggleBreakpointOnCurrentLine: function()
+    {
+        if (this._muted)
+            return;
+
+        var selection = this.textEditor.selection();
+        if (!selection)
+            return;
+        this._toggleBreakpoint(selection.startLine, false);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string} condition
+     * @param {boolean} enabled
+     */
+    _setBreakpoint: function(lineNumber, condition, enabled)
+    {
+        this._breakpointManager.setBreakpoint(this._uiSourceCode, lineNumber, condition, enabled);
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.SetBreakpoint,
+            url: this._uiSourceCode.originURL(),
+            line: lineNumber,
+            enabled: enabled
+        });
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    _continueToLine: function(lineNumber)
+    {
+        var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this._uiSourceCode.uiLocationToRawLocation(lineNumber, 0));
+        WebInspector.debuggerModel.continueToLocation(rawLocation);
+    },
+
+    dispose: function()
+    {
+        this._breakpointManager.removeEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this);
+        this._breakpointManager.removeEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessageAdded, this._consoleMessageAdded, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, this._consoleMessageRemoved, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessagesCleared, this._consoleMessagesCleared, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+        WebInspector.UISourceCodeFrame.prototype.dispose.call(this);
+    },
+
+    __proto__: WebInspector.UISourceCodeFrame.prototype
+}
diff --git a/Source/devtools/front_end/KeyboardShortcut.js b/Source/devtools/front_end/KeyboardShortcut.js
new file mode 100644
index 0000000..96d8e6e
--- /dev/null
+++ b/Source/devtools/front_end/KeyboardShortcut.js
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.KeyboardShortcut = function()
+{
+}
+
+/**
+ * Constants for encoding modifier key set as a bit mask.
+ * @see #_makeKeyFromCodeAndModifiers
+ */
+WebInspector.KeyboardShortcut.Modifiers = {
+    None: 0,   // Constant for empty modifiers set.
+    Shift: 1,
+    Ctrl: 2,
+    Alt: 4,
+    Meta: 8,   // Command key on Mac, Win key on other platforms.
+    get CtrlOrMeta()
+    {
+        // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
+        return WebInspector.isMac() ? this.Meta : this.Ctrl;
+    }
+};
+
+/** @typedef {{code: number, name: (string|Object.<string, string>)}} */
+WebInspector.KeyboardShortcut.Key;
+
+/** @type {!Object.<string, !WebInspector.KeyboardShortcut.Key>} */
+WebInspector.KeyboardShortcut.Keys = {
+    Backspace: { code: 8, name: "\u21a4" },
+    Tab: { code: 9, name: { mac: "\u21e5", other: "<Tab>" } },
+    Enter: { code: 13, name: { mac: "\u21a9", other: "<Enter>" } },
+    Esc: { code: 27, name: { mac: "\u238b", other: "<Esc>" } },
+    Space: { code: 32, name: "<Space>" },
+    PageUp: { code: 33,  name: { mac: "\u21de", other: "<PageUp>" } },      // also NUM_NORTH_EAST
+    PageDown: { code: 34, name: { mac: "\u21df", other: "<PageDown>" } },   // also NUM_SOUTH_EAST
+    End: { code: 35, name: { mac: "\u2197", other: "<End>" } },             // also NUM_SOUTH_WEST
+    Home: { code: 36, name: { mac: "\u2196", other: "<Home>" } },           // also NUM_NORTH_WEST
+    Left: { code: 37, name: "<Left>" },           // also NUM_WEST
+    Up: { code: 38, name: "<Up>" },             // also NUM_NORTH
+    Right: { code: 39, name: "<Right>" },          // also NUM_EAST
+    Down: { code: 40, name: "<Down>" },           // also NUM_SOUTH
+    Delete: { code: 46, name: "<Del>" },
+    Zero: { code: 48, name: "0" },
+    F1: { code: 112, name: "F1" },
+    F2: { code: 113, name: "F2" },
+    F3: { code: 114, name: "F3" },
+    F4: { code: 115, name: "F4" },
+    F5: { code: 116, name: "F5" },
+    F6: { code: 117, name: "F6" },
+    F7: { code: 118, name: "F7" },
+    F8: { code: 119, name: "F8" },
+    F9: { code: 120, name: "F9" },
+    F10: { code: 121, name: "F10" },
+    F11: { code: 122, name: "F11" },
+    F12: { code: 123, name: "F12" },
+    Semicolon: { code: 186, name: ";" },
+    Plus: { code: 187, name: "+" },
+    Comma: { code: 188, name: "," },
+    Minus: { code: 189, name: "-" },
+    Period: { code: 190, name: "." },
+    Slash: { code: 191, name: "/" },
+    Apostrophe: { code: 192, name: "`" },
+    SingleQuote: { code: 222, name: "\'" },
+    H: { code: 72, name: "H" }
+};
+
+/**
+ * Creates a number encoding keyCode in the lower 8 bits and modifiers mask in the higher 8 bits.
+ * It is useful for matching pressed keys.
+ *
+ * @param {number|string} keyCode The Code of the key, or a character "a-z" which is converted to a keyCode value.
+ * @param {number=} modifiers Optional list of modifiers passed as additional paramerters.
+ * @return {number}
+ */
+WebInspector.KeyboardShortcut.makeKey = function(keyCode, modifiers)
+{
+    if (typeof keyCode === "string")
+        keyCode = keyCode.charCodeAt(0) - 32;
+    modifiers = modifiers || WebInspector.KeyboardShortcut.Modifiers.None;
+    return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, modifiers);
+}
+
+/**
+ * @param {KeyboardEvent} keyboardEvent
+ * @return {number}
+ */
+WebInspector.KeyboardShortcut.makeKeyFromEvent = function(keyboardEvent)
+{
+    var modifiers = WebInspector.KeyboardShortcut.Modifiers.None;
+    if (keyboardEvent.shiftKey)
+        modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
+    if (keyboardEvent.ctrlKey)
+        modifiers |= WebInspector.KeyboardShortcut.Modifiers.Ctrl;
+    if (keyboardEvent.altKey)
+        modifiers |= WebInspector.KeyboardShortcut.Modifiers.Alt;
+    if (keyboardEvent.metaKey)
+        modifiers |= WebInspector.KeyboardShortcut.Modifiers.Meta;
+    return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyboardEvent.keyCode, modifiers);
+}
+
+/**
+ * @param {KeyboardEvent} event
+ * @return {boolean}
+ */
+WebInspector.KeyboardShortcut.eventHasCtrlOrMeta = function(event)
+{
+    return WebInspector.isMac() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
+}
+
+/**
+ * @param {KeyboardEvent} event
+ * @return {boolean}
+ */
+WebInspector.KeyboardShortcut.hasNoModifiers = function(event)
+{
+    return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
+}
+
+/** @typedef {{key: number, name: string}} */
+WebInspector.KeyboardShortcut.Descriptor;
+
+/**
+ * @param {string|WebInspector.KeyboardShortcut.Key} key
+ * @param {number=} modifiers
+ * @return {WebInspector.KeyboardShortcut.Descriptor}
+ */
+WebInspector.KeyboardShortcut.makeDescriptor = function(key, modifiers)
+{
+    return {
+        key: WebInspector.KeyboardShortcut.makeKey(typeof key === "string" ? key : key.code, modifiers),
+        name: WebInspector.KeyboardShortcut.shortcutToString(key, modifiers)
+    };
+}
+
+/**
+ * @param {string|WebInspector.KeyboardShortcut.Key} key
+ * @param {number=} modifiers
+ * @return {string}
+ */
+WebInspector.KeyboardShortcut.shortcutToString = function(key, modifiers)
+{
+    return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + WebInspector.KeyboardShortcut._keyName(key);
+}
+
+/**
+ * @param {string|WebInspector.KeyboardShortcut.Key} key
+ * @return {string}
+ */
+WebInspector.KeyboardShortcut._keyName = function(key)
+{
+    if (typeof key === "string")
+        return key.toUpperCase();
+    if (typeof key.name === "string")
+        return key.name;
+    return key.name[WebInspector.platform()] || key.name.other || '';
+}
+
+/**
+ * @param {number} keyCode
+ * @param {?number} modifiers
+ * @return {number}
+ */
+WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers = function(keyCode, modifiers)
+{
+    return (keyCode & 255) | (modifiers << 8);
+};
+
+/**
+ * @param {number|undefined} modifiers
+ * @return {string}
+ */
+WebInspector.KeyboardShortcut._modifiersToString = function(modifiers)
+{
+    const cmdKey = "\u2318";
+    const optKey = "\u2325";
+    const shiftKey = "\u21e7";
+    const ctrlKey = "\u2303";
+
+    var isMac = WebInspector.isMac();
+    var res = "";
+    if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Ctrl)
+        res += isMac ? ctrlKey : "<Ctrl> + ";
+    if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Alt)
+        res += isMac ? optKey : "<Alt> + ";
+    if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Shift)
+        res += isMac ? shiftKey : "<Shift> + ";
+    if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Meta)
+        res += isMac ? cmdKey : "<Win> + ";
+
+    return res;
+};
+
+WebInspector.KeyboardShortcut.SelectAll = WebInspector.KeyboardShortcut.makeKey("a", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
diff --git a/Source/devtools/front_end/Linkifier.js b/Source/devtools/front_end/Linkifier.js
new file mode 100644
index 0000000..0d934fa
--- /dev/null
+++ b/Source/devtools/front_end/Linkifier.js
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.LinkifierFormatter = function()
+{
+}
+
+WebInspector.LinkifierFormatter.prototype = {
+    /**
+     * @param {Element} anchor
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    formatLiveAnchor: function(anchor, uiLocation) { }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.LinkifierFormatter=} formatter
+ */
+WebInspector.Linkifier = function(formatter)
+{
+    this._formatter = formatter || new WebInspector.Linkifier.DefaultFormatter(WebInspector.Linkifier.MaxLengthForDisplayedURLs);
+    this._liveLocations = [];
+}
+
+WebInspector.Linkifier.prototype = {
+    /**
+     * @param {string} sourceURL
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     * @param {string=} classes
+     * @return {Element}
+     */
+    linkifyLocation: function(sourceURL, lineNumber, columnNumber, classes)
+    {
+        var rawLocation = WebInspector.debuggerModel.createRawLocationByURL(sourceURL, lineNumber, columnNumber || 0);
+        if (!rawLocation)
+            return WebInspector.linkifyResourceAsNode(sourceURL, lineNumber, classes);
+        return this.linkifyRawLocation(rawLocation, classes);
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} rawLocation
+     * @param {string=} classes
+     * @return {Element}
+     */
+    linkifyRawLocation: function(rawLocation, classes)
+    {
+        var script = WebInspector.debuggerModel.scriptForId(rawLocation.scriptId);
+        if (!script)
+            return null;
+        var anchor = WebInspector.linkifyURLAsNode("", "", classes, false);
+        var liveLocation = script.createLiveLocation(rawLocation, this._updateAnchor.bind(this, anchor));
+        this._liveLocations.push(liveLocation);
+        return anchor;
+    },
+
+    /**
+     * @param {WebInspector.CSSRule} rule
+     * @return {?Element}
+     */
+    linkifyCSSRuleLocation: function(rule)
+    {
+        var anchor = WebInspector.linkifyURLAsNode("", "", "", false);
+        var liveLocation = WebInspector.cssModel.createLiveLocation(rule, this._updateAnchor.bind(this, anchor));
+        if (!liveLocation)
+            return null;
+        this._liveLocations.push(liveLocation);
+        return anchor;
+    },
+
+    reset: function()
+    {
+        for (var i = 0; i < this._liveLocations.length; ++i)
+            this._liveLocations[i].dispose();
+        this._liveLocations = [];
+    },
+
+    /**
+     * @param {Element} anchor
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    _updateAnchor: function(anchor, uiLocation)
+    {
+        anchor.preferredPanel = "scripts";
+        anchor.href = sanitizeHref(uiLocation.uiSourceCode.originURL());
+        anchor.uiSourceCode = uiLocation.uiSourceCode;
+        anchor.lineNumber = uiLocation.lineNumber;
+        this._formatter.formatLiveAnchor(anchor, uiLocation);
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.LinkifierFormatter}
+ * @param {number=} maxLength
+ */
+WebInspector.Linkifier.DefaultFormatter = function(maxLength)
+{
+    this._maxLength = maxLength;
+}
+
+WebInspector.Linkifier.DefaultFormatter.prototype = {
+    /**
+     * @param {Element} anchor
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    formatLiveAnchor: function(anchor, uiLocation)
+    {
+        var text = uiLocation.linkText();
+        if (this._maxLength)
+            text = text.trimMiddle(this._maxLength);
+        anchor.textContent = text;
+
+        var titleText = uiLocation.uiSourceCode.originURL();
+        if (typeof uiLocation.lineNumber === "number")
+            titleText += ":" + (uiLocation.lineNumber + 1);
+        anchor.title = titleText;
+    },
+
+    __proto__: WebInspector.LinkifierFormatter.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Linkifier.DefaultFormatter}
+ */
+WebInspector.Linkifier.DefaultCSSFormatter = function()
+{
+    WebInspector.Linkifier.DefaultFormatter.call(this);
+}
+
+WebInspector.Linkifier.DefaultCSSFormatter.prototype = {
+    /**
+     * @param {Element} anchor
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    formatLiveAnchor: function(anchor, uiLocation)
+    {
+        WebInspector.Linkifier.DefaultFormatter.prototype.formatLiveAnchor.call(this, anchor, uiLocation);
+        anchor.classList.add("webkit-html-resource-link");
+        anchor.setAttribute("data-uncopyable", anchor.textContent);
+        anchor.textContent = "";
+    },
+    __proto__: WebInspector.Linkifier.DefaultFormatter.prototype
+}
+
+/**
+ * The maximum number of characters to display in a URL.
+ * @const
+ * @type {number}
+ */
+WebInspector.Linkifier.MaxLengthForDisplayedURLs = 150;
diff --git a/Source/devtools/front_end/LiveEditSupport.js b/Source/devtools/front_end/LiveEditSupport.js
new file mode 100644
index 0000000..ace094e
--- /dev/null
+++ b/Source/devtools/front_end/LiveEditSupport.js
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.LiveEditSupport = function(workspace)
+{
+    this._workspaceProvider = new WebInspector.SimpleWorkspaceProvider(workspace, WebInspector.projectTypes.LiveEdit);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
+    this._debuggerReset();
+}
+
+WebInspector.LiveEditSupport.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {WebInspector.UISourceCode}
+     */
+    uiSourceCodeForLiveEdit: function(uiSourceCode)
+    {
+        var rawLocation = uiSourceCode.uiLocationToRawLocation(0, 0);
+        var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation);
+        var script = WebInspector.debuggerModel.scriptForId(debuggerModelLocation.scriptId);
+        var uiLocation = script.rawLocationToUILocation(0, 0);
+
+        // FIXME: Support live editing of scripts mapped to some file.
+        if (uiLocation.uiSourceCode !== uiSourceCode)
+            return uiLocation.uiSourceCode;
+        if (this._uiSourceCodeForScriptId[script.scriptId])
+            return this._uiSourceCodeForScriptId[script.scriptId];
+
+        console.assert(!script.isInlineScript());
+        var liveEditUISourceCode = this._workspaceProvider.addUniqueFileForURL(script.sourceURL, script, true, script.isContentScript);
+
+        liveEditUISourceCode.setScriptFile(new WebInspector.LiveEditScriptFile(uiSourceCode, liveEditUISourceCode, script.scriptId));
+        this._uiSourceCodeForScriptId[script.scriptId] = liveEditUISourceCode;
+        this._scriptIdForUISourceCode.put(liveEditUISourceCode, script.scriptId);
+        return liveEditUISourceCode;
+    },
+
+    _debuggerReset: function()
+    {
+        /** @type {Object.<string, WebInspector.UISourceCode>} */
+        this._uiSourceCodeForScriptId = {};
+        this._scriptIdForUISourceCode = new Map();
+        this._workspaceProvider.reset();
+    },
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ScriptFile}
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ * @param {WebInspector.UISourceCode} liveEditUISourceCode
+ * @param {string} scriptId
+ */
+WebInspector.LiveEditScriptFile = function(uiSourceCode, liveEditUISourceCode, scriptId)
+{
+    WebInspector.ScriptFile.call(this);
+    this._uiSourceCode = uiSourceCode;
+    this._liveEditUISourceCode = liveEditUISourceCode;
+    this._scriptId = scriptId;
+    this._liveEditUISourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+}
+
+WebInspector.LiveEditScriptFile.prototype = {
+    _workingCopyCommitted: function(event)
+    {
+        /**
+         * @param {?string} error
+         */
+        function innerCallback(error)
+        {
+            if (error) {
+                WebInspector.showErrorMessage(error);
+                return;
+            }
+        }
+
+        var script = WebInspector.debuggerModel.scriptForId(this._scriptId);
+        WebInspector.debuggerModel.setScriptSource(script.scriptId, this._liveEditUISourceCode.workingCopy(), innerCallback.bind(this));
+    },
+
+    /**
+     * @return {boolean}
+     */
+    hasDivergedFromVM: function()
+    {
+        return true;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isDivergingFromVM: function()
+    {
+        return false;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isMergingToVM: function()
+    {
+        return false;
+    },
+
+    checkMapping: function()
+    {
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/** @type {WebInspector.LiveEditSupport} */
+WebInspector.liveEditSupport = null;
diff --git a/Source/devtools/front_end/MemoryStatistics.js b/Source/devtools/front_end/MemoryStatistics.js
new file mode 100644
index 0000000..6d5fa7c
--- /dev/null
+++ b/Source/devtools/front_end/MemoryStatistics.js
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @param {WebInspector.TimelinePanel} timelinePanel
+ * @param {WebInspector.TimelineModel} model
+ * @param {number} sidebarWidth
+ * @constructor
+ */
+WebInspector.MemoryStatistics = function(timelinePanel, model, sidebarWidth)
+{
+    this._timelinePanel = timelinePanel;
+    this._counters = [];
+
+    model.addEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onRecordAdded, this);
+    model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._onRecordsCleared, this);
+
+    this._containerAnchor = timelinePanel.element.lastChild;
+    this._memorySidebarView = new WebInspector.SidebarView(WebInspector.SidebarView.SidebarPosition.Start, undefined, sidebarWidth);
+    this._memorySidebarView.sidebarElement.addStyleClass("sidebar");
+    this._memorySidebarView.element.id = "memory-graphs-container";
+
+    this._memorySidebarView.addEventListener(WebInspector.SidebarView.EventTypes.Resized, this._sidebarResized.bind(this));
+
+    this._canvasContainer = this._memorySidebarView.mainElement;
+    this._canvasContainer.id = "memory-graphs-canvas-container";
+    this._createCurrentValuesBar();
+    this._canvas = this._canvasContainer.createChild("canvas");
+    this._canvas.id = "memory-counters-graph";
+    this._lastMarkerXPosition = 0;
+
+    this._canvas.addEventListener("mouseover", this._onMouseOver.bind(this), true);
+    this._canvas.addEventListener("mousemove", this._onMouseMove.bind(this), true);
+    this._canvas.addEventListener("mouseout", this._onMouseOut.bind(this), true);
+    this._canvas.addEventListener("click", this._onClick.bind(this), true);
+    // We create extra timeline grid here to reuse its event dividers.
+    this._timelineGrid = new WebInspector.TimelineGrid();
+    this._canvasContainer.appendChild(this._timelineGrid.dividersElement);
+
+    // Populate sidebar
+    this._memorySidebarView.sidebarElement.createChild("div", "sidebar-tree sidebar-tree-section").textContent = WebInspector.UIString("COUNTERS");
+    this._counterUI = this._createCounterUIList();
+}
+
+/**
+ * @constructor
+ * @param {number} time
+ */
+WebInspector.MemoryStatistics.Counter = function(time)
+{
+    this.time = time;
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.SwatchCheckbox = function(title, color)
+{
+    this.element = document.createElement("div");
+    this._swatch = this.element.createChild("div", "swatch");
+    this.element.createChild("span", "title").textContent = title;
+    this._color = color;
+    this.checked = true;
+
+    this.element.addEventListener("click", this._toggleCheckbox.bind(this), true);
+}
+
+WebInspector.SwatchCheckbox.Events = {
+    Changed: "Changed"
+}
+
+WebInspector.SwatchCheckbox.prototype = {
+    get checked()
+    {
+        return this._checked;
+    },
+
+    set checked(v)
+    {
+        this._checked = v;
+        if (this._checked)
+            this._swatch.style.backgroundColor = this._color;
+        else
+            this._swatch.style.backgroundColor = "";
+    },
+
+    _toggleCheckbox: function(event)
+    {
+        this.checked = !this.checked;
+        this.dispatchEventToListeners(WebInspector.SwatchCheckbox.Events.Changed);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.CounterUIBase = function(memoryCountersPane, title, graphColor, valueGetter)
+{
+    this._memoryCountersPane = memoryCountersPane;
+    this.valueGetter = valueGetter;
+    var container = memoryCountersPane._memorySidebarView.sidebarElement.createChild("div", "memory-counter-sidebar-info");
+    var swatchColor = graphColor;
+    this._swatch = new WebInspector.SwatchCheckbox(WebInspector.UIString(title), swatchColor);
+    this._swatch.addEventListener(WebInspector.SwatchCheckbox.Events.Changed, this._toggleCounterGraph.bind(this));
+    container.appendChild(this._swatch.element);
+
+    this._value = null;
+    this.graphColor =graphColor;
+    this.strokeColor = graphColor;
+    this.graphYValues = [];
+}
+
+WebInspector.CounterUIBase.prototype = {
+    _toggleCounterGraph: function(event)
+    {
+        if (this._swatch.checked)
+            this._value.removeStyleClass("hidden");
+        else
+            this._value.addStyleClass("hidden");
+        this._memoryCountersPane.refresh();
+    },
+
+    updateCurrentValue: function(countersEntry)
+    {
+        this._value.textContent = Number.bytesToString(this.valueGetter(countersEntry));
+    },
+
+    clearCurrentValueAndMarker: function(ctx)
+    {
+        this._value.textContent = "";
+    },
+
+    get visible()
+    {
+        return this._swatch.checked;
+    },
+}
+
+WebInspector.MemoryStatistics.prototype = {
+    _createCurrentValuesBar: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    _createCounterUIList: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    _onRecordsCleared: function()
+    {
+        this._counters = [];
+    },
+
+    /**
+     * @param {WebInspector.TimelineGrid} timelineGrid
+     */
+    setMainTimelineGrid: function(timelineGrid)
+    {
+        this._mainTimelineGrid = timelineGrid;
+    },
+
+    /**
+     * @param {number} top
+     */
+     setTopPosition: function(top)
+    {
+        this._memorySidebarView.element.style.top = top + "px";
+        this._updateSize();
+    },
+
+    /**
+     * @param {number} width
+     */
+    setSidebarWidth: function(width)
+    {
+        if (this._ignoreSidebarResize)
+            return;
+        this._ignoreSidebarResize = true;
+        this._memorySidebarView.setSidebarWidth(width);
+        this._ignoreSidebarResize = false;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _sidebarResized: function(event)
+    {
+        if (this._ignoreSidebarResize)
+            return;
+        this._ignoreSidebarResize = true;
+        this._timelinePanel.splitView.setSidebarWidth(event.data);
+        this._ignoreSidebarResize = false;
+    },
+
+    _canvasHeight: function()
+    {
+        throw new Error("Not implemented");
+    },
+
+    _updateSize: function()
+    {
+        var width = this._mainTimelineGrid.dividersElement.offsetWidth + 1;
+        this._canvasContainer.style.width = width + "px";
+
+        var height = this._canvasHeight();
+        this._canvas.width = width;
+        this._canvas.height = height;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onRecordAdded: function(event)
+    {
+        throw new Error("Not implemented");
+    },
+
+    _draw: function()
+    {
+        this._calculateVisibleIndexes();
+        this._calculateXValues();
+        this._clear();
+
+        this._setVerticalClip(10, this._canvas.height - 20);
+    },
+
+    _calculateVisibleIndexes: function()
+    {
+        var calculator = this._timelinePanel.calculator;
+        var start = calculator.minimumBoundary() * 1000;
+        var end = calculator.maximumBoundary() * 1000;
+        var firstIndex = 0;
+        var lastIndex = this._counters.length - 1;
+        for (var i = 0; i < this._counters.length; i++) {
+            var time = this._counters[i].time;
+            if (time <= start) {
+                firstIndex = i;
+            } else {
+                if (end < time)
+                    break;
+                lastIndex = i;
+            }
+        }
+        // Maximum index of element whose time <= start.
+        this._minimumIndex = firstIndex;
+
+        // Maximum index of element whose time <= end.
+        this._maximumIndex = lastIndex;
+
+        // Current window bounds.
+        this._minTime = start;
+        this._maxTime = end;
+    },
+
+    /**
+     * @param {MouseEvent} event
+     */
+     _onClick: function(event)
+    {
+        var x = event.x - event.target.offsetParent.offsetLeft;
+        var i = this._recordIndexAt(x);
+        var counter = this._counters[i];
+        if (counter)
+            this._timelinePanel.revealRecordAt(counter.time / 1000);
+    },
+
+    /**
+     * @param {MouseEvent} event
+     */
+     _onMouseOut: function(event)
+    {
+        delete this._markerXPosition;
+
+        var ctx = this._canvas.getContext("2d");
+        this._clearCurrentValueAndMarker(ctx);
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     */
+    _clearCurrentValueAndMarker: function(ctx)
+    {
+        for (var i = 0; i < this._counterUI.length; i++)
+            this._counterUI[i].clearCurrentValueAndMarker(ctx);
+    },
+
+    /**
+     * @param {MouseEvent} event
+     */
+     _onMouseOver: function(event)
+    {
+        this._onMouseMove(event);
+    },
+
+    /**
+     * @param {MouseEvent} event
+     */
+     _onMouseMove: function(event)
+    {
+        var x = event.x - event.target.offsetParent.offsetLeft
+        this._markerXPosition = x;
+        this._refreshCurrentValues();
+    },
+
+    _refreshCurrentValues: function()
+    {
+        if (!this._counters.length)
+            return;
+        if (this._markerXPosition === undefined)
+            return;
+        if (this._maximumIndex === -1)
+            return;
+        var i = this._recordIndexAt(this._markerXPosition);
+
+        this._updateCurrentValue(this._counters[i]);
+
+        this._highlightCurrentPositionOnGraphs(this._markerXPosition, i);
+    },
+
+    _updateCurrentValue: function(counterEntry)
+    {
+        for (var j = 0; j < this._counterUI.length; j++)
+            this._counterUI[j].updateCurrentValue(counterEntry);
+    },
+
+    _recordIndexAt: function(x)
+    {
+        var i;
+        for (i = this._minimumIndex + 1; i <= this._maximumIndex; i++) {
+            var statX = this._counters[i].x;
+            if (x < statX)
+                break;
+        }
+        i--;
+        return i;
+    },
+
+    _highlightCurrentPositionOnGraphs: function(x, index)
+    {
+        var ctx = this._canvas.getContext("2d");
+        this._restoreImageUnderMarker(ctx);
+        this._drawMarker(ctx, x, index);
+    },
+
+    _restoreImageUnderMarker: function(ctx)
+    {
+        throw new Error("Not implemented");
+    },
+
+    _drawMarker: function(ctx, x, index)
+    {
+        throw new Error("Not implemented");
+    },
+
+    visible: function()
+    {
+        return this._memorySidebarView.isShowing();
+    },
+
+    show: function()
+    {
+        var anchor = /** @type {Element|null} */ (this._containerAnchor.nextSibling);
+        this._memorySidebarView.show(this._timelinePanel.element, anchor);
+        this._updateSize();
+        this._refreshDividers();
+        setTimeout(this._draw.bind(this), 0);
+    },
+
+    refresh: function()
+    {
+        this._updateSize();
+        this._refreshDividers();
+        this._draw();
+        this._refreshCurrentValues();
+    },
+
+    hide: function()
+    {
+        this._memorySidebarView.detach();
+    },
+
+    _refreshDividers: function()
+    {
+        this._timelineGrid.updateDividers(this._timelinePanel.calculator);
+    },
+
+    _setVerticalClip: function(originY, height)
+    {
+        this._originY = originY;
+        this._clippedHeight = height;
+    },
+
+    _calculateXValues: function()
+    {
+        if (!this._counters.length)
+            return;
+
+        var width = this._canvas.width;
+        var xFactor = width / (this._maxTime - this._minTime);
+
+        this._counters[this._minimumIndex].x = 0;
+        for (var i = this._minimumIndex + 1; i < this._maximumIndex; i++)
+             this._counters[i].x = xFactor * (this._counters[i].time - this._minTime);
+        this._counters[this._maximumIndex].x = width;
+    },
+
+    _clear: function() {
+        var ctx = this._canvas.getContext("2d");
+        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+        this._discardImageUnderMarker();
+    },
+
+    _discardImageUnderMarker: function()
+    {
+        throw new Error("Not implemented");
+    }
+}
+
diff --git a/Source/devtools/front_end/MetricsSidebarPane.js b/Source/devtools/front_end/MetricsSidebarPane.js
new file mode 100644
index 0000000..72b245c
--- /dev/null
+++ b/Source/devtools/front_end/MetricsSidebarPane.js
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2007 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.MetricsSidebarPane = function()
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Metrics"));
+
+    WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetOrMediaQueryResultChanged, this);
+    WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged, this._styleSheetOrMediaQueryResultChanged, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._attributesUpdated, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrRemoved, this._attributesUpdated, this);
+}
+
+WebInspector.MetricsSidebarPane.prototype = {
+    /**
+     * @param {WebInspector.DOMNode=} node
+     */
+    update: function(node)
+    {
+        if (node)
+            this.node = node;
+        this._innerUpdate();
+    },
+
+    _innerUpdate: function()
+    {
+        // "style" attribute might have changed. Update metrics unless they are being edited
+        // (if a CSS property is added, a StyleSheetChanged event is dispatched).
+        if (this._isEditingMetrics)
+            return;
+
+        // FIXME: avoid updates of a collapsed pane.
+        var node = this.node;
+
+        if (!node || node.nodeType() !== Node.ELEMENT_NODE) {
+            this.bodyElement.removeChildren();
+            return;
+        }
+
+        function callback(style)
+        {
+            if (!style || this.node !== node)
+                return;
+            this._updateMetrics(style);
+        }
+        WebInspector.cssModel.getComputedStyleAsync(node.id, callback.bind(this));
+
+        function inlineStyleCallback(style)
+        {
+            if (!style || this.node !== node)
+                return;
+            this.inlineStyle = style;
+        }
+        WebInspector.cssModel.getInlineStylesAsync(node.id, inlineStyleCallback.bind(this));
+    },
+
+    _styleSheetOrMediaQueryResultChanged: function()
+    {
+        this._innerUpdate();
+    },
+
+    _attributesUpdated: function(event)
+    {
+        if (this.node !== event.data.node)
+            return;
+
+        this._innerUpdate();
+    },
+
+    _getPropertyValueAsPx: function(style, propertyName)
+    {
+        return Number(style.getPropertyValue(propertyName).replace(/px$/, "") || 0);
+    },
+
+    _getBox: function(computedStyle, componentName)
+    {
+        var suffix = componentName === "border" ? "-width" : "";
+        var left = this._getPropertyValueAsPx(computedStyle, componentName + "-left" + suffix);
+        var top = this._getPropertyValueAsPx(computedStyle, componentName + "-top" + suffix);
+        var right = this._getPropertyValueAsPx(computedStyle, componentName + "-right" + suffix);
+        var bottom = this._getPropertyValueAsPx(computedStyle, componentName + "-bottom" + suffix);
+        return { left: left, top: top, right: right, bottom: bottom };
+    },
+
+    _highlightDOMNode: function(showHighlight, mode, event)
+    {
+        event.consume();
+        var nodeId = showHighlight && this.node ? this.node.id : 0;
+        if (nodeId) {
+            if (this._highlightMode === mode)
+                return;
+            this._highlightMode = mode;
+            WebInspector.domAgent.highlightDOMNode(nodeId, mode);
+        } else {
+            delete this._highlightMode;
+            WebInspector.domAgent.hideDOMNodeHighlight();
+        }
+
+        for (var i = 0; this._boxElements && i < this._boxElements.length; ++i) {
+            var element = this._boxElements[i];
+            if (!nodeId || mode === "all" || element._name === mode)
+                element.style.backgroundColor = element._backgroundColor;
+            else
+                element.style.backgroundColor = "";
+        }
+    },
+
+    _updateMetrics: function(style)
+    {
+        // Updating with computed style.
+        var metricsElement = document.createElement("div");
+        metricsElement.className = "metrics";
+        var self = this;
+
+        function createBoxPartElement(style, name, side, suffix)
+        {
+            var propertyName = (name !== "position" ? name + "-" : "") + side + suffix;
+            var value = style.getPropertyValue(propertyName);
+            if (value === "" || (name !== "position" && value === "0px"))
+                value = "\u2012";
+            else if (name === "position" && value === "auto")
+                value = "\u2012";
+            value = value.replace(/px$/, "");
+
+            var element = document.createElement("div");
+            element.className = side;
+            element.textContent = value;
+            element.addEventListener("dblclick", this.startEditing.bind(this, element, name, propertyName, style), false);
+            return element;
+        }
+
+        function getContentAreaWidthPx(style)
+        {
+            var width = style.getPropertyValue("width").replace(/px$/, "");
+            if (style.getPropertyValue("box-sizing") === "border-box") {
+                var borderBox = self._getBox(style, "border");
+                var paddingBox = self._getBox(style, "padding");
+
+                width = width - borderBox.left - borderBox.right - paddingBox.left - paddingBox.right;
+            }
+
+            return width;
+        }
+
+        function getContentAreaHeightPx(style)
+        {
+            var height = style.getPropertyValue("height").replace(/px$/, "");
+            if (style.getPropertyValue("box-sizing") === "border-box") {
+                var borderBox = self._getBox(style, "border");
+                var paddingBox = self._getBox(style, "padding");
+
+                height = height - borderBox.top - borderBox.bottom - paddingBox.top - paddingBox.bottom;
+            }
+
+            return height;
+        }
+
+        // Display types for which margin is ignored.
+        var noMarginDisplayType = {
+            "table-cell": true,
+            "table-column": true,
+            "table-column-group": true,
+            "table-footer-group": true,
+            "table-header-group": true,
+            "table-row": true,
+            "table-row-group": true
+        };
+
+        // Display types for which padding is ignored.
+        var noPaddingDisplayType = {
+            "table-column": true,
+            "table-column-group": true,
+            "table-footer-group": true,
+            "table-header-group": true,
+            "table-row": true,
+            "table-row-group": true
+        };
+
+        // Position types for which top, left, bottom and right are ignored.
+        var noPositionType = {
+            "static": true
+        };
+
+        var boxes = ["content", "padding", "border", "margin", "position"];
+        var boxColors = [
+            WebInspector.Color.PageHighlight.Content,
+            WebInspector.Color.PageHighlight.Padding,
+            WebInspector.Color.PageHighlight.Border,
+            WebInspector.Color.PageHighlight.Margin,
+            WebInspector.Color.fromRGBA([0, 0, 0, 0])
+        ];
+        var boxLabels = [WebInspector.UIString("content"), WebInspector.UIString("padding"), WebInspector.UIString("border"), WebInspector.UIString("margin"), WebInspector.UIString("position")];
+        var previousBox = null;
+        this._boxElements = [];
+        for (var i = 0; i < boxes.length; ++i) {
+            var name = boxes[i];
+
+            if (name === "margin" && noMarginDisplayType[style.getPropertyValue("display")])
+                continue;
+            if (name === "padding" && noPaddingDisplayType[style.getPropertyValue("display")])
+                continue;
+            if (name === "position" && noPositionType[style.getPropertyValue("position")])
+                continue;
+
+            var boxElement = document.createElement("div");
+            boxElement.className = name;
+            boxElement._backgroundColor = boxColors[i].toString(WebInspector.Color.Format.RGBA);
+            boxElement._name = name;
+            boxElement.style.backgroundColor = boxElement._backgroundColor;
+            boxElement.addEventListener("mouseover", this._highlightDOMNode.bind(this, true, name === "position" ? "all" : name), false);
+            this._boxElements.push(boxElement);
+
+            if (name === "content") {
+                var widthElement = document.createElement("span");
+                widthElement.textContent = getContentAreaWidthPx(style);
+                widthElement.addEventListener("dblclick", this.startEditing.bind(this, widthElement, "width", "width", style), false);
+
+                var heightElement = document.createElement("span");
+                heightElement.textContent = getContentAreaHeightPx(style);
+                heightElement.addEventListener("dblclick", this.startEditing.bind(this, heightElement, "height", "height", style), false);
+
+                boxElement.appendChild(widthElement);
+                boxElement.appendChild(document.createTextNode(" \u00D7 "));
+                boxElement.appendChild(heightElement);
+            } else {
+                var suffix = (name === "border" ? "-width" : "");
+
+                var labelElement = document.createElement("div");
+                labelElement.className = "label";
+                labelElement.textContent = boxLabels[i];
+                boxElement.appendChild(labelElement);
+
+                boxElement.appendChild(createBoxPartElement.call(this, style, name, "top", suffix));
+                boxElement.appendChild(document.createElement("br"));
+                boxElement.appendChild(createBoxPartElement.call(this, style, name, "left", suffix));
+
+                if (previousBox)
+                    boxElement.appendChild(previousBox);
+
+                boxElement.appendChild(createBoxPartElement.call(this, style, name, "right", suffix));
+                boxElement.appendChild(document.createElement("br"));
+                boxElement.appendChild(createBoxPartElement.call(this, style, name, "bottom", suffix));
+            }
+
+            previousBox = boxElement;
+        }
+
+        metricsElement.appendChild(previousBox);
+        metricsElement.addEventListener("mouseover", this._highlightDOMNode.bind(this, false, ""), false);
+        this.bodyElement.removeChildren();
+        this.bodyElement.appendChild(metricsElement);
+    },
+
+    startEditing: function(targetElement, box, styleProperty, computedStyle)
+    {
+        if (WebInspector.isBeingEdited(targetElement))
+            return;
+
+        var context = { box: box, styleProperty: styleProperty, computedStyle: computedStyle };
+        var boundKeyDown = this._handleKeyDown.bind(this, context, styleProperty);
+        context.keyDownHandler = boundKeyDown;
+        targetElement.addEventListener("keydown", boundKeyDown, false);
+
+        this._isEditingMetrics = true;
+
+        var config = new WebInspector.EditingConfig(this.editingCommitted.bind(this), this.editingCancelled.bind(this), context);
+        WebInspector.startEditing(targetElement, config);
+
+        window.getSelection().setBaseAndExtent(targetElement, 0, targetElement, 1);
+    },
+
+    _handleKeyDown: function(context, styleProperty, event)
+    {
+        var element = event.currentTarget;
+
+        function finishHandler(originalValue, replacementString)
+        {
+            this._applyUserInput(element, replacementString, originalValue, context, false);
+        }
+
+        function customNumberHandler(number)
+        {
+            if (styleProperty !== "margin" && number < 0)
+                number = 0;
+            return number;
+        }
+
+        WebInspector.handleElementValueModifications(event, element, finishHandler.bind(this), undefined, customNumberHandler);
+    },
+
+    editingEnded: function(element, context)
+    {
+        delete this.originalPropertyData;
+        delete this.previousPropertyDataCandidate;
+        element.removeEventListener("keydown", context.keyDownHandler, false);
+        delete this._isEditingMetrics;
+    },
+
+    editingCancelled: function(element, context)
+    {
+        if ("originalPropertyData" in this && this.inlineStyle) {
+            if (!this.originalPropertyData) {
+                // An added property, remove the last property in the style.
+                var pastLastSourcePropertyIndex = this.inlineStyle.pastLastSourcePropertyIndex();
+                if (pastLastSourcePropertyIndex)
+                    this.inlineStyle.allProperties[pastLastSourcePropertyIndex - 1].setText("", false);
+            } else
+                this.inlineStyle.allProperties[this.originalPropertyData.index].setText(this.originalPropertyData.propertyText, false);
+        }
+        this.editingEnded(element, context);
+        this.update();
+    },
+
+    _applyUserInput: function(element, userInput, previousContent, context, commitEditor)
+    {
+        if (!this.inlineStyle) {
+            // Element has no renderer.
+            return this.editingCancelled(element, context); // nothing changed, so cancel
+        }
+
+        if (commitEditor && userInput === previousContent)
+            return this.editingCancelled(element, context); // nothing changed, so cancel
+
+        if (context.box !== "position" && (!userInput || userInput === "\u2012"))
+            userInput = "0px";
+        else if (context.box === "position" && (!userInput || userInput === "\u2012"))
+            userInput = "auto";
+
+        userInput = userInput.toLowerCase();
+        // Append a "px" unit if the user input was just a number.
+        if (/^\d+$/.test(userInput))
+            userInput += "px";
+
+        var styleProperty = context.styleProperty;
+        var computedStyle = context.computedStyle;
+
+        if (computedStyle.getPropertyValue("box-sizing") === "border-box" && (styleProperty === "width" || styleProperty === "height")) {
+            if (!userInput.match(/px$/)) {
+                WebInspector.log("For elements with box-sizing: border-box, only absolute content area dimensions can be applied", WebInspector.ConsoleMessage.MessageLevel.Error, true);
+                return;
+            }
+
+            var borderBox = this._getBox(computedStyle, "border");
+            var paddingBox = this._getBox(computedStyle, "padding");
+            var userValuePx = Number(userInput.replace(/px$/, ""));
+            if (isNaN(userValuePx))
+                return;
+            if (styleProperty === "width")
+                userValuePx += borderBox.left + borderBox.right + paddingBox.left + paddingBox.right;
+            else
+                userValuePx += borderBox.top + borderBox.bottom + paddingBox.top + paddingBox.bottom;
+
+            userInput = userValuePx + "px";
+        }
+
+        this.previousPropertyDataCandidate = null;
+        var self = this;
+        var callback = function(style) {
+            if (!style)
+                return;
+            self.inlineStyle = style;
+            if (!("originalPropertyData" in self))
+                self.originalPropertyData = self.previousPropertyDataCandidate;
+
+            if (typeof self._highlightMode !== "undefined") {
+                WebInspector.domAgent.highlightDOMNode(self.node.id, self._highlightMode);
+            }
+
+            if (commitEditor) {
+                self.dispatchEventToListeners("metrics edited");
+                self.update();
+            }
+        };
+
+        var allProperties = this.inlineStyle.allProperties;
+        for (var i = 0; i < allProperties.length; ++i) {
+            var property = allProperties[i];
+            if (property.name !== context.styleProperty || property.inactive)
+                continue;
+
+            this.previousPropertyDataCandidate = property;
+            property.setValue(userInput, commitEditor, true, callback);
+            return;
+        }
+
+        this.inlineStyle.appendProperty(context.styleProperty, userInput, callback);
+    },
+
+    editingCommitted: function(element, userInput, previousContent, context)
+    {
+        this.editingEnded(element, context);
+        this._applyUserInput(element, userInput, previousContent, context, true);
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
diff --git a/Source/devtools/front_end/NativeBreakpointsSidebarPane.js b/Source/devtools/front_end/NativeBreakpointsSidebarPane.js
new file mode 100644
index 0000000..6e723c4
--- /dev/null
+++ b/Source/devtools/front_end/NativeBreakpointsSidebarPane.js
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.NativeBreakpointsSidebarPane = function(title)
+{
+    WebInspector.SidebarPane.call(this, title);
+    this.registerRequiredCSS("breakpointsList.css");
+
+    this.listElement = document.createElement("ol");
+    this.listElement.className = "breakpoint-list";
+
+    this.emptyElement = document.createElement("div");
+    this.emptyElement.className = "info";
+    this.emptyElement.textContent = WebInspector.UIString("No Breakpoints");
+
+    this.bodyElement.appendChild(this.emptyElement);
+}
+
+WebInspector.NativeBreakpointsSidebarPane.prototype = {
+    _addListElement: function(element, beforeElement)
+    {
+        if (beforeElement)
+            this.listElement.insertBefore(element, beforeElement);
+        else {
+            if (!this.listElement.firstChild) {
+                this.bodyElement.removeChild(this.emptyElement);
+                this.bodyElement.appendChild(this.listElement);
+            }
+            this.listElement.appendChild(element);
+        }
+    },
+
+    _removeListElement: function(element)
+    {
+        this.listElement.removeChild(element);
+        if (!this.listElement.firstChild) {
+            this.bodyElement.removeChild(this.listElement);
+            this.bodyElement.appendChild(this.emptyElement);
+        }
+    },
+
+    _reset: function()
+    {
+        this.listElement.removeChildren();
+        if (this.listElement.parentElement) {
+            this.bodyElement.removeChild(this.listElement);
+            this.bodyElement.appendChild(this.emptyElement);
+        }
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
diff --git a/Source/devtools/front_end/NativeHeapSnapshot.js b/Source/devtools/front_end/NativeHeapSnapshot.js
new file mode 100644
index 0000000..722ee43
--- /dev/null
+++ b/Source/devtools/front_end/NativeHeapSnapshot.js
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshot}
+ */
+WebInspector.NativeHeapSnapshot = function(profile)
+{
+    WebInspector.HeapSnapshot.call(this, profile);
+    this._nodeObjectType = this._metaNode.type_strings["object"];
+    this._edgeWeakType = this._metaNode.type_strings["weak"];
+    this._edgeElementType = this._metaNode.type_strings["property"];
+}
+
+WebInspector.NativeHeapSnapshot.prototype = {
+    createNode: function(nodeIndex)
+    {
+        return new WebInspector.NativeHeapSnapshotNode(this, nodeIndex);
+    },
+
+    createEdge: function(edges, edgeIndex)
+    {
+        return new WebInspector.NativeHeapSnapshotEdge(this, edges, edgeIndex);
+    },
+
+    createRetainingEdge: function(retainedNodeIndex, retainerIndex)
+    {
+        return new WebInspector.NativeHeapSnapshotRetainerEdge(this, retainedNodeIndex, retainerIndex);
+    },
+
+    _markInvisibleEdges: function()
+    {
+    },
+
+    _calculateFlags: function()
+    {
+    },
+
+    userObjectsMapAndFlag: function()
+    {
+        return null;
+    },
+
+    images: function()
+    {
+        var aggregatesByClassName = this.aggregates(false, "allObjects");
+        var result = [];
+        var cachedImages = aggregatesByClassName["WebCore::CachedImage"];
+        function getImageName(node)
+        {
+            return node.name();
+        }
+        this._addNodes(cachedImages, getImageName, result);
+
+        var canvases = aggregatesByClassName["WebCore::HTMLCanvasElement"];
+        function getCanvasName(node)
+        {
+            return "HTMLCanvasElement";
+        }
+        this._addNodes(canvases, getCanvasName, result);
+        return result;
+    },
+
+    _addNodes: function(classData, nameResolver, result)
+    {
+        if (!classData)
+            return;
+        var node = this.rootNode();
+        for (var i = 0; i < classData.idxs.length; i++) {
+            node.nodeIndex = classData.idxs[i];
+            result.push({
+                name: nameResolver(node),
+                size: node.retainedSize(),
+            });
+        }
+    },
+
+    __proto__: WebInspector.HeapSnapshot.prototype
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotNode}
+ * @param {WebInspector.NativeHeapSnapshot} snapshot
+ * @param {number=} nodeIndex
+ */
+WebInspector.NativeHeapSnapshotNode = function(snapshot, nodeIndex)
+{
+    WebInspector.HeapSnapshotNode.call(this, snapshot, nodeIndex)
+}
+
+WebInspector.NativeHeapSnapshotNode.prototype = {
+    className: function()
+    {
+        return this._snapshot._strings[this.classIndex()];
+    },
+
+    classIndex: function()
+    {
+        return this._snapshot._nodes[this.nodeIndex + this._snapshot._nodeTypeOffset];
+    },
+
+    id: function()
+    {
+        return this._snapshot._nodes[this.nodeIndex + this._snapshot._nodeIdOffset];
+    },
+
+    name: function()
+    {
+        return this._snapshot._strings[this._snapshot._nodes[this.nodeIndex + this._snapshot._nodeNameOffset]];;
+    },
+
+    serialize: function()
+    {
+        return {
+            id: this.id(),
+            name: this.className(),
+            displayName: this.name(),
+            distance: this.distance(),
+            nodeIndex: this.nodeIndex,
+            retainedSize: this.retainedSize(),
+            selfSize: this.selfSize(),
+            type: this._snapshot._nodeObjectType
+       };
+    },
+
+    isHidden: function()
+    {
+        return false;
+    },
+
+    isSynthetic: function()
+    {
+        return false;
+    },
+
+    __proto__: WebInspector.HeapSnapshotNode.prototype
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotEdge}
+ * @param {WebInspector.NativeHeapSnapshot} snapshot
+ * @param {Array.<number>} edges
+ * @param {number=} edgeIndex
+ */
+WebInspector.NativeHeapSnapshotEdge = function(snapshot, edges, edgeIndex)
+{
+    WebInspector.HeapSnapshotEdge.call(this, snapshot, edges, edgeIndex);
+}
+
+WebInspector.NativeHeapSnapshotEdge.prototype = {
+    clone: function()
+    {
+        return new WebInspector.NativeHeapSnapshotEdge(this._snapshot, this._edges, this.edgeIndex);
+    },
+
+    hasStringName: function()
+    {
+        return true;
+    },
+
+    isHidden: function()
+    {
+        return false;
+    },
+
+    isWeak: function()
+    {
+        return false;
+    },
+
+    isInternal: function()
+    {
+        return false;
+    },
+
+    isInvisible: function()
+    {
+        return false;
+    },
+
+    isShortcut: function()
+    {
+        return false;
+    },
+
+    name: function()
+    {
+        return this._snapshot._strings[this._nameOrIndex()];
+    },
+
+    toString: function()
+    {
+        return  "NativeHeapSnapshotEdge: " + this.name();
+    },
+
+    _nameOrIndex: function()
+    {
+        return this._edges.item(this.edgeIndex + this._snapshot._edgeNameOffset);
+    },
+
+    __proto__: WebInspector.HeapSnapshotEdge.prototype
+};
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotRetainerEdge}
+ * @param {WebInspector.NativeHeapSnapshot} snapshot
+ */
+WebInspector.NativeHeapSnapshotRetainerEdge = function(snapshot, retainedNodeIndex, retainerIndex)
+{
+    WebInspector.HeapSnapshotRetainerEdge.call(this, snapshot, retainedNodeIndex, retainerIndex);
+}
+
+WebInspector.NativeHeapSnapshotRetainerEdge.prototype = {
+    clone: function()
+    {
+        return new WebInspector.NativeHeapSnapshotRetainerEdge(this._snapshot, this._retainedNodeIndex, this.retainerIndex());
+    },
+
+    isHidden: function()
+    {
+        return this._edge().isHidden();
+    },
+
+    isInternal: function()
+    {
+        return this._edge().isInternal();
+    },
+
+    isInvisible: function()
+    {
+        return this._edge().isInvisible();
+    },
+
+    isShortcut: function()
+    {
+        return this._edge().isShortcut();
+    },
+
+    isWeak: function()
+    {
+        return this._edge().isWeak();
+    },
+
+    __proto__: WebInspector.HeapSnapshotRetainerEdge.prototype
+}
+
diff --git a/Source/devtools/front_end/NativeMemoryGraph.js b/Source/devtools/front_end/NativeMemoryGraph.js
new file mode 100644
index 0000000..c3d84ea
--- /dev/null
+++ b/Source/devtools/front_end/NativeMemoryGraph.js
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @param {WebInspector.TimelinePanel} timelinePanel
+ * @param {WebInspector.TimelineModel} model
+ * @param {number} sidebarWidth
+ * @constructor
+ * @extends {WebInspector.MemoryStatistics}
+ */
+WebInspector.NativeMemoryGraph = function(timelinePanel, model, sidebarWidth)
+{
+    WebInspector.MemoryStatistics.call(this, timelinePanel, model, sidebarWidth);
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.MemoryStatistics.Counter}
+ */
+WebInspector.NativeMemoryGraph.Counter = function(time, nativeCounters)
+{
+    WebInspector.MemoryStatistics.Counter.call(this, time);
+    this.nativeCounters = nativeCounters;
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.CounterUIBase}
+ * @param {WebInspector.NativeMemoryGraph} memoryCountersPane
+ * @param {string} title
+ * @param {Array.<number>} hsl
+ * @param {function(WebInspector.NativeMemoryGraph.Counter):number} valueGetter
+ */
+WebInspector.NativeMemoryCounterUI = function(memoryCountersPane, title, hsl, valueGetter)
+{
+    var swatchColor = this._hslToString(hsl);
+    WebInspector.CounterUIBase.call(this, memoryCountersPane, title, swatchColor, valueGetter);
+    this._value = this._swatch.element.createChild("span", "memory-category-value");
+
+    const borderLightnessDifference = 3;
+    hsl[2] -= borderLightnessDifference;
+    this.strokeColor = this._hslToString(hsl);
+    this.graphYValues = [];
+}
+
+WebInspector.NativeMemoryCounterUI.prototype = {
+    _hslToString: function(hsl)
+    {
+        return "hsl(" + hsl[0] + "," + hsl[1] + "%," + hsl[2] + "%)";
+    },
+
+    updateCurrentValue: function(countersEntry)
+    {
+        var bytes = this.valueGetter(countersEntry);
+        var megabytes =  bytes / (1024 * 1024);
+        this._value.textContent = WebInspector.UIString("%.1f\u2009MB", megabytes);
+    },
+
+    clearCurrentValueAndMarker: function(ctx)
+    {
+        this._value.textContent = "";
+    },
+
+    __proto__: WebInspector.CounterUIBase.prototype
+}
+
+
+WebInspector.NativeMemoryGraph.prototype = {
+    _createCurrentValuesBar: function()
+    {
+    },
+
+    _createCounterUIList: function()
+    {
+        var nativeCounters = [
+            "JSExternalResources",
+            "CSS",
+            "GlyphCache",
+            "Image",
+            "Resources",
+            "DOM",
+            "Rendering",
+            "Audio",
+            "WebInspector",
+            "JSHeap.Used",
+            "JSHeap.Unused",
+            "MallocWaste",
+            "Other",
+            "PrivateBytes",
+        ];
+
+        /**
+         * @param {string} name
+         * @return {number}
+         */
+        function getCounterValue(name, entry)
+        {
+            return (entry.nativeCounters && entry.nativeCounters[name]) || 0;
+        }
+
+        var list = [];
+        for (var i = nativeCounters.length - 1; i >= 0; i--) {
+            var name = nativeCounters[i];
+            if ("PrivateBytes" === name) {
+                var counterUI = new WebInspector.NativeMemoryCounterUI(this, "Total", [0, 0, 0], getCounterValue.bind(this, name))
+                this._privateBytesCounter = counterUI;
+            } else {
+                var counterUI = new WebInspector.NativeMemoryCounterUI(this, name, [i * 20, 65, 63], getCounterValue.bind(this, name))
+                list.push(counterUI);
+            }
+        }
+        return list.reverse();
+    },
+
+    _canvasHeight: function()
+    {
+        return this._canvasContainer.offsetHeight;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onRecordAdded: function(event)
+    {
+        var statistics = this._counters;
+        function addStatistics(record)
+        {
+            var nativeCounters = record["nativeHeapStatistics"];
+            if (!nativeCounters)
+                return;
+
+            var knownSize = 0;
+            for (var name in nativeCounters) {
+                if (name === "PrivateBytes")
+                    continue;
+                knownSize += nativeCounters[name];
+            }
+            nativeCounters["Other"] = nativeCounters["PrivateBytes"] - knownSize;
+
+            statistics.push(new WebInspector.NativeMemoryGraph.Counter(
+                record.endTime || record.startTime,
+                nativeCounters
+            ));
+        }
+        WebInspector.TimelinePresentationModel.forAllRecords([event.data], null, addStatistics);
+    },
+
+    _draw: function()
+    {
+        WebInspector.MemoryStatistics.prototype._draw.call(this);
+
+        var maxValue = this._maxCounterValue();
+        this._resetTotalValues();
+
+        var previousCounterUI;
+        for (var i = 0; i < this._counterUI.length; i++) {
+            this._drawGraph(this._counterUI[i], previousCounterUI, maxValue);
+            if (this._counterUI[i].visible)
+                previousCounterUI = this._counterUI[i];
+        }
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     */
+    _clearCurrentValueAndMarker: function(ctx)
+    {
+        WebInspector.MemoryStatistics.prototype._clearCurrentValueAndMarker.call(this, ctx);
+        this._privateBytesCounter.clearCurrentValueAndMarker(ctx);
+    },
+
+    _updateCurrentValue: function(counterEntry)
+    {
+        WebInspector.MemoryStatistics.prototype._updateCurrentValue.call(this, counterEntry);
+        this._privateBytesCounter.updateCurrentValue(counterEntry);
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     */
+    _restoreImageUnderMarker: function(ctx)
+    {
+        if (this._imageUnderMarker)
+            ctx.putImageData(this._imageUnderMarker.imageData, this._imageUnderMarker.x, this._imageUnderMarker.y);
+        this._discardImageUnderMarker();
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     * @param {number} left
+     * @param {number} top
+     * @param {number} right
+     * @param {number} bottom
+     */
+    _saveImageUnderMarker: function(ctx, left, top, right, bottom)
+    {
+        var imageData = ctx.getImageData(left, top, right, bottom);
+        this._imageUnderMarker = {
+            x: left,
+            y: top,
+            imageData: imageData
+        };
+    },
+
+    /**
+     * @param {CanvasRenderingContext2D} ctx
+     * @param {number} x
+     * @param {number} index
+     */
+    _drawMarker: function(ctx, x, index)
+    {
+        var left = this._counters[index].x;
+        var right = index + 1 < this._counters.length ? this._counters[index + 1].x : left;
+        var top = this._originY;
+        top = 0;
+        var bottom = top + this._clippedHeight;
+        bottom += this._originY;
+
+        this._saveImageUnderMarker(ctx, left, top, right, bottom);
+
+        ctx.beginPath();
+        ctx.moveTo(left, top);
+        ctx.lineTo(right, top);
+        ctx.lineTo(right, bottom);
+        ctx.lineTo(left, bottom);
+        ctx.lineWidth = 1;
+        ctx.closePath();
+        ctx.fillStyle = "rgba(220,220,220,0.3)";
+        ctx.fill();
+    },
+
+    /**
+     * @return {number}
+     */
+    _maxCounterValue: function()
+    {
+        if (!this._counters.length)
+            return 0;
+
+        var valueGetter = this._privateBytesCounter.valueGetter;
+        var result = 0;
+        for (var i = this._minimumIndex; i < this._maximumIndex; i++) {
+            var counter = this._counters[i];
+            var value = valueGetter(counter);
+            if (value > result)
+                result = value;
+        }
+        return result;
+    },
+
+    _resetTotalValues: function()
+    {
+        for (var i = this._minimumIndex; i <= this._maximumIndex; i++) {
+            var counter = this._counters[i];
+            counter.total = 0;
+        }
+    },
+
+    /**
+     * @param {WebInspector.CounterUIBase} counterUI
+     * @param {WebInspector.CounterUIBase} previousCounterUI
+     * @param {number} maxTotalValue
+     */
+    _drawGraph: function(counterUI, previousCounterUI, maxTotalValue)
+    {
+        var canvas = this._canvas;
+        var ctx = canvas.getContext("2d");
+        var width = canvas.width;
+        var height = this._clippedHeight;
+        var originY = this._originY;
+        var valueGetter = counterUI.valueGetter;
+
+        if (!this._counters.length)
+            return;
+
+        if (!counterUI.visible)
+            return;
+
+        for (var i = this._minimumIndex; i <= this._maximumIndex; i++) {
+            var counter = this._counters[i];
+            var value = valueGetter(counter);
+            counter.total += value;
+        }
+
+        var yValues = counterUI.graphYValues;
+        yValues.length = this._counters.length;
+
+        var maxYRange =  maxTotalValue;
+        var yFactor = maxYRange ? height / (maxYRange) : 1;
+
+        ctx.beginPath();
+        if (previousCounterUI) {
+            var prevYValues = previousCounterUI.graphYValues;
+            var currentY = prevYValues[this._maximumIndex];
+            ctx.moveTo(width, currentY);
+            var currentX = width;
+            for (var i = this._maximumIndex - 1; i >= this._minimumIndex; i--) {
+                currentY = prevYValues[i];
+                currentX = this._counters[i].x;
+                ctx.lineTo(currentX, currentY);
+            }
+        } else {
+            var lastY = originY + height;
+            ctx.moveTo(width, lastY);
+            ctx.lineTo(0, lastY);
+        }
+
+        var currentY = originY + (height - this._counters[this._minimumIndex].total * yFactor);
+        ctx.lineTo(0, currentY);
+        for (var i = this._minimumIndex; i <= this._maximumIndex; i++) {
+             var counter = this._counters[i];
+             var x = counter.x;
+             currentY = originY + (height - counter.total * yFactor);
+             ctx.lineTo(x, currentY);
+
+             yValues[i] = currentY;
+        }
+        ctx.lineTo(width, currentY);
+        ctx.closePath();
+        ctx.lineWidth = 1;
+
+        ctx.strokeStyle = counterUI.strokeColor;
+        ctx.fillStyle = counterUI.graphColor;
+        ctx.fill();
+        ctx.stroke();
+    },
+
+    _discardImageUnderMarker: function()
+    {
+        delete this._imageUnderMarker;
+    },
+
+    __proto__: WebInspector.MemoryStatistics.prototype
+}
+
diff --git a/Source/devtools/front_end/NativeMemorySnapshotView.js b/Source/devtools/front_end/NativeMemorySnapshotView.js
new file mode 100644
index 0000000..ab67d26
--- /dev/null
+++ b/Source/devtools/front_end/NativeMemorySnapshotView.js
@@ -0,0 +1,713 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.NativeMemoryProfileHeader} profile
+ */
+WebInspector.NativeMemorySnapshotView = function(profile)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("nativeMemoryProfiler.css");
+
+    this.element.addStyleClass("native-snapshot-view");
+    this._containmentDataGrid = new WebInspector.NativeSnapshotDataGrid(profile);
+    this._containmentDataGrid.show(this.element);
+}
+
+WebInspector.NativeMemorySnapshotView.prototype = {
+    __proto__: WebInspector.View.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGrid}
+ * @param {WebInspector.NativeMemoryProfileHeader} profile
+ */
+WebInspector.NativeSnapshotDataGrid = function(profile)
+{
+    var columns = [
+        {id: "name", title: WebInspector.UIString("Object"), width: "200px", disclosure: true, sortable: true},
+        {id: "size", title: WebInspector.UIString("Size"), sortable: true, sort: WebInspector.DataGrid.Order.Descending},
+    ];
+    WebInspector.DataGrid.call(this, columns);
+    this._profile = profile;
+    this._totalNode = new WebInspector.NativeSnapshotNode(profile._memoryBlock, profile);
+    if (WebInspector.settings.showNativeSnapshotUninstrumentedSize.get()) {
+        this.setRootNode(new WebInspector.DataGridNode(null, true));
+        this.rootNode().appendChild(this._totalNode)
+        this._totalNode.expand();
+    } else {
+        this.setRootNode(this._totalNode);
+        this._totalNode.populate();
+    }
+    this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this.sortingChanged.bind(this), this);
+}
+
+WebInspector.NativeSnapshotDataGrid.prototype = {
+    sortingChanged: function()
+    {
+        var expandedNodes = {};
+        this._totalNode._storeState(expandedNodes);
+        this._totalNode.removeChildren();
+        this._totalNode._populated = false;
+        this._totalNode.populate();
+        this._totalNode._shouldRefreshChildren = true;
+        this._totalNode._restoreState(expandedNodes);
+    },
+
+    /**
+     * @param {MemoryAgent.MemoryBlock} nodeA
+     * @param {MemoryAgent.MemoryBlock} nodeB
+     */
+    _sortingFunction: function(nodeA, nodeB)
+    {
+        var sortColumnIdentifier = this.sortColumnIdentifier();
+        var sortAscending = this.isSortOrderAscending();
+        var field1 = nodeA[sortColumnIdentifier];
+        var field2 = nodeB[sortColumnIdentifier];
+        var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
+        if (!sortAscending)
+            result = -result;
+        return result;
+    },
+
+    __proto__: WebInspector.DataGrid.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ * @param {MemoryAgent.MemoryBlock} nodeData
+ */
+WebInspector.NativeSnapshotNode = function(nodeData, profile)
+{
+    this._nodeData = nodeData;
+    this._profile = profile;
+    var viewProperties = WebInspector.MemoryBlockViewProperties._forMemoryBlock(nodeData);
+    var data = { name: viewProperties._description, size: this._nodeData.size };
+    var hasChildren = this._addChildrenFromGraph();
+    WebInspector.DataGridNode.call(this, data, hasChildren);
+}
+
+WebInspector.NativeSnapshotNode.prototype = {
+    /**
+     * @override
+     * @param {string} columnIdentifier
+     * @return {Element}
+     */
+    createCell: function(columnIdentifier)
+    {
+        var cell = columnIdentifier === "size" ?
+            this._createSizeCell(columnIdentifier) :
+            WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
+        return cell;
+    },
+
+    /**
+     * @param {Object} expandedNodes
+     */
+    _storeState: function(expandedNodes)
+    {
+        if (!this.expanded)
+            return;
+        expandedNodes[this.uid()] = true;
+        for (var i in this.children)
+            this.children[i]._storeState(expandedNodes);
+    },
+
+    /**
+     * @param {Object} expandedNodes
+     */
+    _restoreState: function(expandedNodes)
+    {
+        if (!expandedNodes[this.uid()])
+            return;
+        this.expand();
+        for (var i in this.children)
+            this.children[i]._restoreState(expandedNodes);
+    },
+
+    /**
+     * @return {string}
+     */
+    uid: function()
+    {
+        if (!this._uid)
+            this._uid = (!this.parent || !this.parent.uid ? "" : this.parent.uid() || "") + "/" + this._nodeData.name;
+        return this._uid;
+    },
+
+    /**
+     * @param {string} columnIdentifier
+     * @return {Element}
+     */
+    _createSizeCell: function(columnIdentifier)
+    {
+        var node = this;
+        var viewProperties = null;
+        var dimmed = false;
+        while (!viewProperties || viewProperties._fillStyle === "inherit") {
+            viewProperties = WebInspector.MemoryBlockViewProperties._forMemoryBlock(node._nodeData);
+            if (viewProperties._fillStyle === "inherit")
+                dimmed = true;
+            node = node.parent;
+        }
+
+        var sizeKB = this._nodeData.size / 1024;
+        var totalSize = this._profile._memoryBlock.size;
+        var percentage = this._nodeData.size / totalSize  * 100;
+
+        var cell = document.createElement("td");
+        cell.className = columnIdentifier + "-column";
+
+        var textDiv = document.createElement("div");
+        textDiv.textContent = Number.withThousandsSeparator(sizeKB.toFixed(0)) + "\u2009" + WebInspector.UIString("KB");
+        textDiv.className = "size-text";
+        cell.appendChild(textDiv);
+
+        var barDiv = document.createElement("div");
+        barDiv.className = "size-bar";
+        barDiv.style.width = percentage + "%";
+        barDiv.style.backgroundColor = viewProperties._fillStyle;
+        // fillerDiv displaces percentage text out of the bar visible area if it doesn't fit.
+        var fillerDiv = document.createElement("div");
+        fillerDiv.className = "percent-text"
+        barDiv.appendChild(fillerDiv);
+        var percentDiv = document.createElement("div");
+        percentDiv.textContent = percentage.toFixed(1) + "%";
+        percentDiv.className = "percent-text"
+        barDiv.appendChild(percentDiv);
+
+        var barHolderDiv = document.createElement("div");
+        if (dimmed)
+            barHolderDiv.className = "dimmed";
+        barHolderDiv.appendChild(barDiv);
+        cell.appendChild(barHolderDiv);
+
+        return cell;
+    },
+
+    populate: function() {
+        if (this._populated)
+            return;
+        this._populated = true;
+        if (this._nodeData.children)
+            this._addChildren();
+    },
+
+    _addChildren: function()
+    {
+        this._nodeData.children.sort(this.dataGrid._sortingFunction.bind(this.dataGrid));
+
+        for (var node in this._nodeData.children) {
+            var nodeData = this._nodeData.children[node];
+            if (WebInspector.settings.showNativeSnapshotUninstrumentedSize.get() || nodeData.name !== "Other")
+                this.appendChild(new WebInspector.NativeSnapshotNode(nodeData, this._profile));
+        }
+    },
+
+    _addChildrenFromGraph: function()
+    {
+        var memoryBlock = this._nodeData;
+        if (memoryBlock.children)
+             return memoryBlock.children.length > 0;
+        if (memoryBlock.name === "Image") {
+            this._addImageDetails();
+            return true;
+        }
+        return false;
+    },
+
+    _addImageDetails: function()
+    {
+        /**
+         * @param {WebInspector.NativeHeapSnapshotProxy} proxy
+         */
+        function didLoad(proxy)
+        {
+            function didReceiveImages(result)
+            {
+                this._nodeData.children = result;
+                if (this.expanded)
+                    this._addChildren();
+            }
+            proxy.images(didReceiveImages.bind(this));
+
+        }
+        this._profile.load(didLoad.bind(this));
+    },
+
+    __proto__: WebInspector.DataGridNode.prototype
+}
+
+
+/**
+ * @constructor
+ * @implements {MemoryAgent.Dispatcher}
+ */
+WebInspector.MemoryAgentDispatcher = function()
+{
+    InspectorBackend.registerMemoryDispatcher(this);
+    this._currentProfileHeader = null;
+}
+
+WebInspector.MemoryAgentDispatcher.instance = function()
+{
+    if (!WebInspector.MemoryAgentDispatcher._instance)
+        WebInspector.MemoryAgentDispatcher._instance = new WebInspector.MemoryAgentDispatcher();
+    return WebInspector.MemoryAgentDispatcher._instance;
+}
+
+WebInspector.MemoryAgentDispatcher.prototype = {
+    /**
+     * @override
+     * @param {MemoryAgent.HeapSnapshotChunk} chunk
+     */
+    addNativeSnapshotChunk: function(chunk)
+    {
+        if (this._currentProfileHeader)
+            this._currentProfileHeader.addNativeSnapshotChunk(chunk);
+    },
+
+    _onRemoveProfileHeader: function(event)
+    {
+        if (event.data === this._currentProfileHeader)
+            this._currentProfileHeader = null;
+    }
+};
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileType}
+ * @param {string} id
+ * @param {string} name
+ */
+WebInspector.NativeProfileTypeBase = function(profileHeaderConstructor, id, name)
+{
+    WebInspector.ProfileType.call(this, id, name);
+    this._profileHeaderConstructor = profileHeaderConstructor;
+    this._nextProfileUid = 1;
+    this.addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader,
+                          WebInspector.MemoryAgentDispatcher.prototype._onRemoveProfileHeader,
+                          WebInspector.MemoryAgentDispatcher.instance());
+}
+
+WebInspector.NativeProfileTypeBase.prototype = {
+    /**
+     * @override
+     * @return {boolean}
+     */
+    isInstantProfile: function()
+    {
+        return true;
+    },
+
+    /**
+     * @override
+     * @return {boolean}
+     */
+    buttonClicked: function()
+    {
+        if (WebInspector.MemoryAgentDispatcher.instance()._currentProfileHeader)
+            return false;
+
+        var profileHeader = new this._profileHeaderConstructor(this, WebInspector.UIString("Snapshot %d", this._nextProfileUid), this._nextProfileUid);
+        ++this._nextProfileUid;
+        profileHeader.isTemporary = true;
+        this.addProfile(profileHeader);
+        WebInspector.MemoryAgentDispatcher.instance()._currentProfileHeader = profileHeader;
+        profileHeader.load(function() { });
+
+
+        /**
+         * @param {?string} error
+         * @param {?MemoryAgent.MemoryBlock} memoryBlock
+         * @param {Object=} graphMetaInformation
+         */
+        function didReceiveMemorySnapshot(error, memoryBlock, graphMetaInformation)
+        {
+            console.assert(this === WebInspector.MemoryAgentDispatcher.instance()._currentProfileHeader);
+            WebInspector.MemoryAgentDispatcher.instance()._currentProfileHeader = null;
+            this._didReceiveMemorySnapshot(error, memoryBlock, graphMetaInformation);
+        }
+        MemoryAgent.getProcessMemoryDistribution(true, didReceiveMemorySnapshot.bind(profileHeader));
+        return false;
+    },
+
+    /**
+     * @override
+     * @param {!WebInspector.ProfileHeader} profile
+     */
+    removeProfile: function(profile)
+    {
+        if (WebInspector.MemoryAgentDispatcher.instance()._currentProfileHeader === profile)
+            WebInspector.MemoryAgentDispatcher.instance()._currentProfileHeader = null;
+        WebInspector.ProfileType.prototype.removeProfile.call(this, profile);
+    },
+
+    /**
+     * @override
+     * @param {string=} title
+     * @return {WebInspector.ProfileHeader}
+     */
+    createTemporaryProfile: function(title)
+    {
+        title = title || WebInspector.UIString("Snapshotting\u2026");
+        return new this._profileHeaderConstructor(this, title);
+    },
+
+    /**
+     * @override
+     * @param {ProfilerAgent.ProfileHeader} profile
+     * @return {WebInspector.ProfileHeader}
+     */
+    createProfile: function(profile)
+    {
+        return new this._profileHeaderConstructor(this, profile.title, -1);
+    },
+
+    __proto__: WebInspector.ProfileType.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NativeProfileTypeBase}
+ */
+WebInspector.NativeSnapshotProfileType = function()
+{
+    WebInspector.NativeProfileTypeBase.call(this, WebInspector.NativeSnapshotProfileHeader,  WebInspector.NativeSnapshotProfileType.TypeId, WebInspector.UIString("Take Native Heap Snapshot"));
+}
+
+WebInspector.NativeSnapshotProfileType.TypeId = "NATIVE_SNAPSHOT";
+
+WebInspector.NativeSnapshotProfileType.prototype = {
+    get buttonTooltip()
+    {
+        return WebInspector.UIString("Capture native heap graph.");
+    },
+
+    get treeItemTitle()
+    {
+        return WebInspector.UIString("NATIVE SNAPSHOT");
+    },
+
+    get description()
+    {
+        return WebInspector.UIString("Native memory snapshot profiles show native heap graph.");
+    },
+
+    __proto__: WebInspector.NativeProfileTypeBase.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapProfileHeader}
+ * @param {!WebInspector.ProfileType} type
+ * @param {string} title
+ * @param {number=} uid
+ */
+WebInspector.NativeSnapshotProfileHeader = function(type, title, uid)
+{
+    WebInspector.HeapProfileHeader.call(this, type, title, uid, 0);
+    this._strings = [];
+    this._nodes = [];
+    this._edges = [];
+    this._baseToRealNodeId = [];
+}
+
+WebInspector.NativeSnapshotProfileHeader.prototype = {
+    /**
+     * @override
+     * @param {!WebInspector.ProfilesPanel} profilesPanel
+     */
+    createView: function(profilesPanel)
+    {
+        return new WebInspector.NativeHeapSnapshotView(profilesPanel, this);
+    },
+
+    startSnapshotTransfer: function()
+    {
+    },
+
+    snapshotConstructorName: function()
+    {
+        return "NativeHeapSnapshot";
+    },
+
+    snapshotProxyConstructor: function()
+    {
+        return WebInspector.NativeHeapSnapshotProxy;
+    },
+
+    addNativeSnapshotChunk: function(chunk)
+    {
+        this._strings = this._strings.concat(chunk.strings);
+        this._nodes = this._nodes.concat(chunk.nodes);
+        this._edges = this._edges.concat(chunk.edges);
+        this._baseToRealNodeId = this._baseToRealNodeId.concat(chunk.baseToRealNodeId);
+    },
+
+    /**
+     * @param {?string} error
+     * @param {?MemoryAgent.MemoryBlock} memoryBlock
+     * @param {Object=} graphMetaInformation
+     */
+    _didReceiveMemorySnapshot: function(error, memoryBlock, graphMetaInformation)
+    {
+        var metaInformation = /** @type{HeapSnapshotMetainfo} */ (graphMetaInformation);
+        this.isTemporary = false;
+
+        var edgeFieldCount = metaInformation.edge_fields.length;
+        var nodeFieldCount = metaInformation.node_fields.length;
+        var nodeIdFieldOffset = metaInformation.node_fields.indexOf("id");
+        var toNodeIdFieldOffset = metaInformation.edge_fields.indexOf("to_node");
+
+        var baseToRealNodeIdMap = {};
+        for (var i = 0; i < this._baseToRealNodeId.length; i += 2)
+            baseToRealNodeIdMap[this._baseToRealNodeId[i]] = this._baseToRealNodeId[i + 1];
+
+        var nodeId2NodeIndex = {};
+        for (var i = nodeIdFieldOffset; i < this._nodes.length; i += nodeFieldCount)
+            nodeId2NodeIndex[this._nodes[i]] = i - nodeIdFieldOffset;
+
+        // Translate nodeId to nodeIndex.
+        var edges = this._edges;
+        for (var i = toNodeIdFieldOffset; i < edges.length; i += edgeFieldCount) {
+            if (edges[i] in baseToRealNodeIdMap)
+                edges[i] = baseToRealNodeIdMap[edges[i]];
+            edges[i] = nodeId2NodeIndex[edges[i]];
+        }
+
+        var heapSnapshot = {
+            "snapshot": {
+                "meta": metaInformation,
+                node_count: this._nodes.length / nodeFieldCount,
+                edge_count: this._edges.length / edgeFieldCount,
+                root_index: this._nodes.length - nodeFieldCount
+            },
+            nodes: this._nodes,
+            edges: this._edges,
+            strings: this._strings
+        };
+
+        var chunk = JSON.stringify(heapSnapshot);
+        this.transferChunk(chunk);
+        this.finishHeapSnapshot();
+    },
+
+    __proto__: WebInspector.HeapProfileHeader.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.HeapSnapshotView}
+ * @param {!WebInspector.ProfilesPanel} parent
+ * @param {!WebInspector.NativeSnapshotProfileHeader} profile
+ */
+WebInspector.NativeHeapSnapshotView = function(parent, profile)
+{
+    this._profile = profile;
+    WebInspector.HeapSnapshotView.call(this, parent, profile);
+}
+
+
+WebInspector.NativeHeapSnapshotView.prototype = {
+    get profile()
+    {
+        return this._profile;
+    },
+
+    __proto__: WebInspector.HeapSnapshotView.prototype
+};
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.NativeProfileTypeBase}
+ */
+WebInspector.NativeMemoryProfileType = function()
+{
+    WebInspector.NativeProfileTypeBase.call(this, WebInspector.NativeMemoryProfileHeader, WebInspector.NativeMemoryProfileType.TypeId, WebInspector.UIString("Capture Native Memory Distribution"));
+}
+
+WebInspector.NativeMemoryProfileType.TypeId = "NATIVE_MEMORY_DISTRIBUTION";
+
+WebInspector.NativeMemoryProfileType.prototype = {
+    get buttonTooltip()
+    {
+        return WebInspector.UIString("Capture native memory distribution.");
+    },
+
+    get treeItemTitle()
+    {
+        return WebInspector.UIString("MEMORY DISTRIBUTION");
+    },
+
+    get description()
+    {
+        return WebInspector.UIString("Native memory snapshot profiles show memory distribution among browser subsystems.");
+    },
+
+    __proto__: WebInspector.NativeProfileTypeBase.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NativeSnapshotProfileHeader}
+ * @param {!WebInspector.ProfileType} type
+ * @param {string} title
+ * @param {number=} uid
+ */
+WebInspector.NativeMemoryProfileHeader = function(type, title, uid)
+{
+    WebInspector.NativeSnapshotProfileHeader.call(this, type, title, uid);
+
+    /**
+     * @type {MemoryAgent.MemoryBlock}
+     */
+    this._memoryBlock = null;
+}
+
+WebInspector.NativeMemoryProfileHeader.prototype = {
+    /**
+     * @override
+     */
+    createSidebarTreeElement: function()
+    {
+        return new WebInspector.ProfileSidebarTreeElement(this, WebInspector.UIString("Snapshot %d"), "heap-snapshot-sidebar-tree-item");
+    },
+
+    /**
+     * @override
+     * @param {WebInspector.ProfilesPanel} profilesPanel
+     */
+    createView: function(profilesPanel)
+    {
+        return new WebInspector.NativeMemorySnapshotView(this);
+    },
+
+    /**
+     * @override
+     */
+    _updateSnapshotStatus: function()
+    {
+        WebInspector.NativeSnapshotProfileHeader.prototype._updateSnapshotStatus.call(this);
+        this.sidebarElement.subtitle = Number.bytesToString(/** @type{number} */ (this._memoryBlock.size));
+    },
+
+    /**
+     * @override
+     * @param {?string} error
+     * @param {?MemoryAgent.MemoryBlock} memoryBlock
+     * @param {Object=} graphMetaInformation
+     */
+    _didReceiveMemorySnapshot: function(error, memoryBlock, graphMetaInformation)
+    {
+        WebInspector.NativeSnapshotProfileHeader.prototype._didReceiveMemorySnapshot.call(this, error, memoryBlock, graphMetaInformation);
+        if (memoryBlock.size && memoryBlock.children) {
+            var knownSize = 0;
+            for (var i = 0; i < memoryBlock.children.length; i++) {
+                var size = memoryBlock.children[i].size;
+                if (size)
+                    knownSize += size;
+            }
+            var otherSize = memoryBlock.size - knownSize;
+
+            if (otherSize) {
+                memoryBlock.children.push({
+                    name: "Other",
+                    size: otherSize
+                });
+            }
+        }
+        this._memoryBlock = memoryBlock;
+    },
+
+    __proto__: WebInspector.NativeSnapshotProfileHeader.prototype
+}
+
+/**
+ * @constructor
+ * @param {string} fillStyle
+ * @param {string} name
+ * @param {string} description
+ */
+WebInspector.MemoryBlockViewProperties = function(fillStyle, name, description)
+{
+    this._fillStyle = fillStyle;
+    this._name = name;
+    this._description = description;
+}
+
+/**
+ * @type {Object.<string, WebInspector.MemoryBlockViewProperties>}
+ */
+WebInspector.MemoryBlockViewProperties._standardBlocks = null;
+
+WebInspector.MemoryBlockViewProperties._initialize = function()
+{
+    if (WebInspector.MemoryBlockViewProperties._standardBlocks)
+        return;
+    WebInspector.MemoryBlockViewProperties._standardBlocks = {};
+    function addBlock(fillStyle, name, description)
+    {
+        WebInspector.MemoryBlockViewProperties._standardBlocks[name] = new WebInspector.MemoryBlockViewProperties(fillStyle, name, WebInspector.UIString(description));
+    }
+    addBlock("hsl(  0,  0%,  60%)", "ProcessPrivateMemory", "Total");
+    addBlock("hsl(  0,  0%,  80%)", "OwnersTypePlaceholder", "OwnersTypePlaceholder");
+    addBlock("hsl(  0,  0%,  60%)", "Other", "Other");
+    addBlock("hsl(220, 80%,  70%)", "Image", "Images");
+    addBlock("hsl(100, 60%,  50%)", "JSHeap", "JavaScript heap");
+    addBlock("hsl( 90, 40%,  80%)", "JSExternalResources", "JavaScript external resources");
+    addBlock("hsl( 90, 60%,  80%)", "CSS", "CSS");
+    addBlock("hsl(  0, 50%,  60%)", "DOM", "DOM");
+    addBlock("hsl(  0, 80%,  60%)", "WebInspector", "Inspector data");
+    addBlock("hsl( 36, 90%,  50%)", "Resources", "Resources");
+    addBlock("hsl( 40, 80%,  80%)", "GlyphCache", "Glyph cache resources");
+    addBlock("hsl( 35, 80%,  80%)", "DOMStorageCache", "DOM storage cache");
+    addBlock("hsl( 60, 80%,  60%)", "RenderTree", "Render tree");
+    addBlock("hsl( 20, 80%,  50%)", "MallocWaste", "Memory allocator waste");
+}
+
+WebInspector.MemoryBlockViewProperties._forMemoryBlock = function(memoryBlock)
+{
+    WebInspector.MemoryBlockViewProperties._initialize();
+    var result = WebInspector.MemoryBlockViewProperties._standardBlocks[memoryBlock.name];
+    if (result)
+        return result;
+    return new WebInspector.MemoryBlockViewProperties("inherit", memoryBlock.name, memoryBlock.name);
+}
+
diff --git a/Source/devtools/front_end/NavigatorOverlayController.js b/Source/devtools/front_end/NavigatorOverlayController.js
new file mode 100644
index 0000000..01280c5
--- /dev/null
+++ b/Source/devtools/front_end/NavigatorOverlayController.js
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.SidebarView} parentSidebarView
+ * @param {WebInspector.View} navigatorView
+ * @param {WebInspector.View} editorView
+ */
+WebInspector.NavigatorOverlayController = function(parentSidebarView, navigatorView, editorView)
+{
+    this._parentSidebarView = parentSidebarView;
+    this._navigatorView = navigatorView;
+    this._editorView = editorView;
+
+    this._navigatorSidebarResizeWidgetElement = document.createElement("div");
+    this._navigatorSidebarResizeWidgetElement.addStyleClass("scripts-navigator-resizer-widget");
+    this._parentSidebarView.installResizer(this._navigatorSidebarResizeWidgetElement);
+    this._navigatorView.element.appendChild(this._navigatorSidebarResizeWidgetElement);
+
+    this._navigatorShowHideButton = new WebInspector.StatusBarButton(WebInspector.UIString("Hide navigator"), "scripts-navigator-show-hide-button", 3);
+    this._navigatorShowHideButton.state = "pinned";
+    this._navigatorShowHideButton.addEventListener("click", this._toggleNavigator, this);
+    this._editorView.element.appendChild(this._navigatorShowHideButton.element);
+
+    WebInspector.settings.navigatorHidden = WebInspector.settings.createSetting("navigatorHidden", true);
+    if (WebInspector.settings.navigatorHidden.get())
+        this._toggleNavigator();
+}
+
+WebInspector.NavigatorOverlayController.prototype = {
+    wasShown: function()
+    {
+        window.setTimeout(this._maybeShowNavigatorOverlay.bind(this), 0);
+    },
+
+    _maybeShowNavigatorOverlay: function()
+    {
+        if (WebInspector.settings.navigatorHidden.get() && !WebInspector.settings.navigatorWasOnceHidden.get())
+            this.showNavigatorOverlay();
+    },
+
+    _toggleNavigator: function()
+    {
+        if (this._navigatorShowHideButton.state === "overlay")
+            this._pinNavigator();
+        else if (this._navigatorShowHideButton.state === "hidden")
+            this.showNavigatorOverlay();
+        else
+            this._hidePinnedNavigator();
+    },
+
+    _hidePinnedNavigator: function()
+    {
+        this._navigatorShowHideButton.state = "hidden";
+        this._navigatorShowHideButton.title = WebInspector.UIString("Show navigator");
+        this._parentSidebarView.element.appendChild(this._navigatorShowHideButton.element);
+
+        this._editorView.element.addStyleClass("navigator-hidden");
+        this._navigatorSidebarResizeWidgetElement.addStyleClass("hidden");
+
+        this._parentSidebarView.hideSidebarElement();
+        this._navigatorView.detach();
+        this._editorView.focus();
+
+        WebInspector.settings.navigatorWasOnceHidden.set(true);
+        WebInspector.settings.navigatorHidden.set(true);
+    },
+
+    _pinNavigator: function()
+    {
+        this._navigatorShowHideButton.state = "pinned";
+        this._navigatorShowHideButton.title = WebInspector.UIString("Hide navigator");
+
+        this._editorView.element.removeStyleClass("navigator-hidden");
+        this._navigatorSidebarResizeWidgetElement.removeStyleClass("hidden");
+        this._editorView.element.appendChild(this._navigatorShowHideButton.element);
+
+        this._innerHideNavigatorOverlay();
+        this._parentSidebarView.showSidebarElement();
+        this._navigatorView.show(this._parentSidebarView.sidebarElement);
+        this._navigatorView.focus();
+        WebInspector.settings.navigatorHidden.set(false);
+    },
+
+    showNavigatorOverlay: function()
+    {
+        if (this._navigatorShowHideButton.state === "overlay")
+            return;
+
+        this._navigatorShowHideButton.state = "overlay";
+        this._navigatorShowHideButton.title = WebInspector.UIString("Pin navigator");
+
+        this._sidebarOverlay = new WebInspector.SidebarOverlay(this._navigatorView, "scriptsPanelNavigatorOverlayWidth", Preferences.minScriptsSidebarWidth);
+        this._boundKeyDown = this._keyDown.bind(this);
+        this._sidebarOverlay.element.addEventListener("keydown", this._boundKeyDown, false);
+        var navigatorOverlayResizeWidgetElement = document.createElement("div");
+        navigatorOverlayResizeWidgetElement.addStyleClass("scripts-navigator-resizer-widget");
+        this._sidebarOverlay.resizerWidgetElement = navigatorOverlayResizeWidgetElement;
+
+        this._navigatorView.element.appendChild(this._navigatorShowHideButton.element);
+        this._boundContainingElementFocused = this._containingElementFocused.bind(this);
+        this._parentSidebarView.element.addEventListener("mousedown", this._boundContainingElementFocused, false);
+
+        this._sidebarOverlay.show(this._parentSidebarView.element);
+        this._navigatorView.focus();
+    },
+
+    _keyDown: function(event)
+    {
+        if (event.handled)
+            return;
+
+        if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
+            this.hideNavigatorOverlay();
+            event.consume(true);
+        }
+    },
+
+    hideNavigatorOverlay: function()
+    {
+        if (this._navigatorShowHideButton.state !== "overlay")
+            return;
+
+        this._navigatorShowHideButton.state = "hidden";
+        this._navigatorShowHideButton.title = WebInspector.UIString("Show navigator");
+        this._parentSidebarView.element.appendChild(this._navigatorShowHideButton.element);
+
+        this._innerHideNavigatorOverlay();
+        this._editorView.focus();
+    },
+
+    _innerHideNavigatorOverlay: function()
+    {
+        this._parentSidebarView.element.removeEventListener("mousedown", this._boundContainingElementFocused, false);
+        this._sidebarOverlay.element.removeEventListener("keydown", this._boundKeyDown, false);
+        this._sidebarOverlay.hide();
+    },
+
+    _containingElementFocused: function(event)
+    {
+        if (!event.target.isSelfOrDescendant(this._sidebarOverlay.element))
+            this.hideNavigatorOverlay();
+    },
+    
+    isNavigatorPinned: function()
+    {
+        return this._navigatorShowHideButton.state === "pinned";
+    },
+    
+    isNavigatorHidden: function()
+    {
+        return this._navigatorShowHideButton.state === "hidden";
+    }
+}
diff --git a/Source/devtools/front_end/NavigatorView.js b/Source/devtools/front_end/NavigatorView.js
new file mode 100644
index 0000000..850b2cf
--- /dev/null
+++ b/Source/devtools/front_end/NavigatorView.js
@@ -0,0 +1,1066 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @extends {WebInspector.View}
+ * @constructor
+ */
+WebInspector.NavigatorView = function()
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("navigatorView.css");
+
+    var scriptsTreeElement = document.createElement("ol");
+    this._scriptsTree = new WebInspector.NavigatorTreeOutline(scriptsTreeElement);
+    this._scriptsTree.childrenListElement.addEventListener("keypress", this._treeKeyPress.bind(this), true);
+
+    var scriptsOutlineElement = document.createElement("div");
+    scriptsOutlineElement.addStyleClass("outline-disclosure");
+    scriptsOutlineElement.addStyleClass("navigator");
+    scriptsOutlineElement.appendChild(scriptsTreeElement);
+
+    this.element.addStyleClass("fill");
+    this.element.addStyleClass("navigator-container");
+    this.element.appendChild(scriptsOutlineElement);
+    this.setDefaultFocusedElement(this._scriptsTree.element);
+
+    /** @type {Object.<string, WebInspector.NavigatorUISourceCodeTreeNode>} */
+    this._uiSourceCodeNodes = {};
+
+    this._rootNode = new WebInspector.NavigatorRootTreeNode(this);
+    this._rootNode.populate();
+
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._inspectedURLChanged, this);
+}
+
+WebInspector.NavigatorView.Events = {
+    ItemSelected: "ItemSelected",
+    ItemSearchStarted: "ItemSearchStarted",
+    FileRenamed: "FileRenamed"
+}
+
+WebInspector.NavigatorView.iconClassForType = function(type)
+{
+    if (type === WebInspector.NavigatorTreeOutline.Types.Domain)
+        return "navigator-domain-tree-item";
+    if (type === WebInspector.NavigatorTreeOutline.Types.FileSystem)
+        return "navigator-folder-tree-item";
+    return "navigator-folder-tree-item";
+}
+
+WebInspector.NavigatorView.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    addUISourceCode: function(uiSourceCode)
+    {
+        var node = this._getOrCreateUISourceCodeParentNode(uiSourceCode);
+        var uiSourceCodeNode = new WebInspector.NavigatorUISourceCodeTreeNode(this, uiSourceCode);
+        this._uiSourceCodeNodes[uiSourceCode.uri()] = uiSourceCodeNode;
+        node.appendChild(uiSourceCodeNode);
+        if (uiSourceCode.url === WebInspector.inspectedPageURL)
+            this.revealUISourceCode(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _inspectedURLChanged: function(event)
+    {
+        var nodes = Object.values(this._uiSourceCodeNodes);
+        for (var i = 0; i < nodes.length; ++i) {
+            var uiSourceCode = nodes[i].uiSourceCode();
+            if (uiSourceCode.url === WebInspector.inspectedPageURL)
+                this.revealUISourceCode(uiSourceCode);
+        }
+
+    },
+
+    /**
+     * @param {WebInspector.Project} project
+     * @return {WebInspector.NavigatorTreeNode}
+     */
+    _getProjectNode: function(project)
+    {
+        if (!project.displayName())
+            return this._rootNode;
+        return this._rootNode.child(project.id());
+    },
+
+    /**
+     * @param {WebInspector.Project} project
+     * @return {WebInspector.NavigatorFolderTreeNode}
+     */
+    _createProjectNode: function(project)
+    {
+        var type = project.type() === WebInspector.projectTypes.FileSystem ? WebInspector.NavigatorTreeOutline.Types.FileSystem : WebInspector.NavigatorTreeOutline.Types.Domain;
+        var projectNode = new WebInspector.NavigatorFolderTreeNode(this, project.id(), type, project.displayName());
+        this._rootNode.appendChild(projectNode);
+        return projectNode;
+    },
+
+    /**
+     * @param {WebInspector.Project} project
+     * @return {WebInspector.NavigatorTreeNode}
+     */
+    _getOrCreateProjectNode: function(project)
+    {
+        return this._getProjectNode(project) || this._createProjectNode(project);
+    },
+
+    /**
+     * @param {WebInspector.NavigatorTreeNode} parentNode
+     * @param {string} name
+     * @return {WebInspector.NavigatorFolderTreeNode}
+     */
+    _getFolderNode: function(parentNode, name)
+    {
+        return parentNode.child(name);
+    },
+
+    /**
+     * @param {WebInspector.NavigatorTreeNode} parentNode
+     * @param {string} name
+     * @return {WebInspector.NavigatorFolderTreeNode}
+     */
+    _createFolderNode: function(parentNode, name)
+    {
+        var folderNode = new WebInspector.NavigatorFolderTreeNode(this, name, WebInspector.NavigatorTreeOutline.Types.Folder, name);
+        parentNode.appendChild(folderNode);
+        return folderNode;
+    },
+
+    /**
+     * @param {WebInspector.NavigatorTreeNode} parentNode
+     * @param {string} name
+     * @return {WebInspector.NavigatorFolderTreeNode}
+     */
+    _getOrCreateFolderNode: function(parentNode, name)
+    {
+        return this._getFolderNode(parentNode, name) || this._createFolderNode(parentNode, name);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {WebInspector.NavigatorTreeNode}
+     */
+    _getUISourceCodeParentNode: function(uiSourceCode)
+    {
+        var projectNode = this._getProjectNode(uiSourceCode.project());
+        if (!projectNode)
+            return null;
+        var path = uiSourceCode.path();
+        var parentNode = projectNode;
+        for (var i = 0; i < path.length - 1; ++i) {
+            parentNode = this._getFolderNode(parentNode, path[i]);
+            if (!parentNode)
+                return null;
+        }
+        return parentNode;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {WebInspector.NavigatorTreeNode}
+     */
+    _getOrCreateUISourceCodeParentNode: function(uiSourceCode)
+    {
+        var projectNode = this._getOrCreateProjectNode(uiSourceCode.project());
+        if (!projectNode)
+            return null;
+        var path = uiSourceCode.path();
+        var parentNode = projectNode;
+        for (var i = 0; i < path.length - 1; ++i) {
+            parentNode = this._getOrCreateFolderNode(parentNode, path[i]);
+            if (!parentNode)
+                return null;
+        }
+        return parentNode;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {boolean=} select
+     */
+    revealUISourceCode: function(uiSourceCode, select)
+    {
+        var node = this._uiSourceCodeNodes[uiSourceCode.uri()];
+        if (!node)
+            return null;
+        if (this._scriptsTree.selectedTreeElement)
+            this._scriptsTree.selectedTreeElement.deselect();
+        this._lastSelectedUISourceCode = uiSourceCode;
+        node.reveal(select);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {boolean} focusSource
+     */
+    _scriptSelected: function(uiSourceCode, focusSource)
+    {
+        this._lastSelectedUISourceCode = uiSourceCode;
+        var data = { uiSourceCode: uiSourceCode, focusSource: focusSource};
+        this.dispatchEventToListeners(WebInspector.NavigatorView.Events.ItemSelected, data);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    removeUISourceCode: function(uiSourceCode)
+    {
+        var parentNode = this._getUISourceCodeParentNode(uiSourceCode);
+        if (!parentNode)
+            return;
+        var node = this._uiSourceCodeNodes[uiSourceCode.uri()];
+        if (!node)
+            return;
+        delete this._uiSourceCodeNodes[uiSourceCode.uri()]
+        parentNode.removeChild(node);
+        node = parentNode;
+        while (node) {
+            parentNode = node.parent;
+            if (!parentNode || !node.isEmpty())
+                break;
+            parentNode.removeChild(node);
+            node = parentNode;
+        }
+    },
+
+    _fileRenamed: function(uiSourceCode, newTitle)
+    {    
+        var data = { uiSourceCode: uiSourceCode, name: newTitle };
+        this.dispatchEventToListeners(WebInspector.NavigatorView.Events.FileRenamed, data);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    handleRename: function(uiSourceCode, callback)
+    {
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {function(boolean)=} callback
+     */
+    rename: function(uiSourceCode, callback)
+    {
+        var node = this._uiSourceCodeNodes[uiSourceCode.uri()];
+        if (!node)
+            return null;
+        node.rename(callback);
+    },
+
+    reset: function()
+    {
+        for (var uri in this._uiSourceCodeNodes)
+            this._uiSourceCodeNodes[uri].dispose();
+
+        this._scriptsTree.removeChildren();
+        this._uiSourceCodeNodes = {};
+        this._rootNode.reset();
+    },
+
+    handleContextMenu: function(event, uiSourceCode)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendApplicableItems(uiSourceCode);
+        contextMenu.show();
+    },
+
+   _treeKeyPress: function(event)
+   {
+        if (WebInspector.isBeingEdited(this._scriptsTree.childrenListElement))
+            return;
+
+        var searchText = String.fromCharCode(event.charCode);
+        if (searchText.trim() !== searchText)
+            return;
+        this.dispatchEventToListeners(WebInspector.NavigatorView.Events.ItemSearchStarted, searchText);
+        event.consume(true);
+   },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeOutline}
+ * @param {Element} element
+ */
+WebInspector.NavigatorTreeOutline = function(element)
+{
+    TreeOutline.call(this, element);
+    this.element = element;
+
+    this.comparator = WebInspector.NavigatorTreeOutline._treeElementsCompare;
+}
+
+WebInspector.NavigatorTreeOutline.Types = {
+    Root: "Root",
+    Domain: "Domain",
+    Folder: "Folder",
+    UISourceCode: "UISourceCode",
+    FileSystem: "FileSystem"
+}
+
+WebInspector.NavigatorTreeOutline._treeElementsCompare = function compare(treeElement1, treeElement2)
+{
+    // Insert in the alphabetical order, first domains, then folders, then scripts.
+    function typeWeight(treeElement)
+    {
+        var type = treeElement.type();
+        if (type === WebInspector.NavigatorTreeOutline.Types.Domain) {
+            if (treeElement.titleText === WebInspector.inspectedPageDomain)
+                return 1;
+            return 2;
+        }
+        if (type === WebInspector.NavigatorTreeOutline.Types.FileSystem)
+            return 3;
+        if (type === WebInspector.NavigatorTreeOutline.Types.Folder)
+            return 4;
+        return 5;
+    }
+
+    var typeWeight1 = typeWeight(treeElement1);
+    var typeWeight2 = typeWeight(treeElement2);
+
+    var result;
+    if (typeWeight1 > typeWeight2)
+        result = 1;
+    else if (typeWeight1 < typeWeight2)
+        result = -1;
+    else {
+        var title1 = treeElement1.titleText;
+        var title2 = treeElement2.titleText;
+        result = title1.compareTo(title2);
+    }
+    return result;
+}
+
+WebInspector.NavigatorTreeOutline.prototype = {
+   /**
+    * @return {Array.<WebInspector.UISourceCode>}
+    */
+   scriptTreeElements: function()
+   {
+       var result = [];
+       if (this.children.length) {
+           for (var treeElement = this.children[0]; treeElement; treeElement = treeElement.traverseNextTreeElement(false, this, true)) {
+               if (treeElement instanceof WebInspector.NavigatorSourceTreeElement)
+                   result.push(treeElement.uiSourceCode);
+           }
+       }
+       return result;
+   },
+
+    __proto__: TreeOutline.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {string} type
+ * @param {string} title
+ * @param {Array.<string>} iconClasses
+ * @param {boolean} hasChildren
+ * @param {boolean=} noIcon
+ */
+WebInspector.BaseNavigatorTreeElement = function(type, title, iconClasses, hasChildren, noIcon)
+{
+    this._type = type;
+    TreeElement.call(this, "", null, hasChildren);
+    this._titleText = title;
+    this._iconClasses = iconClasses;
+    this._noIcon = noIcon;
+}
+
+WebInspector.BaseNavigatorTreeElement.prototype = {
+    onattach: function()
+    {
+        this.listItemElement.removeChildren();
+        if (this._iconClasses) {
+            for (var i = 0; i < this._iconClasses.length; ++i)
+                this.listItemElement.addStyleClass(this._iconClasses[i]);
+        }
+
+        var selectionElement = document.createElement("div");
+        selectionElement.className = "selection";
+        this.listItemElement.appendChild(selectionElement);
+
+        if (!this._noIcon) {
+            this.imageElement = document.createElement("img");
+            this.imageElement.className = "icon";
+            this.listItemElement.appendChild(this.imageElement);
+        }
+        
+        this.titleElement = document.createElement("div");
+        this.titleElement.className = "base-navigator-tree-element-title";
+        this._titleTextNode = document.createTextNode("");
+        this._titleTextNode.textContent = this._titleText;
+        this.titleElement.appendChild(this._titleTextNode);
+        this.listItemElement.appendChild(this.titleElement);
+    },
+
+    onreveal: function()
+    {
+        if (this.listItemElement)
+            this.listItemElement.scrollIntoViewIfNeeded(true);
+    },
+
+    /**
+     * @return {string}
+     */
+    get titleText()
+    {
+        return this._titleText;
+    },
+
+    set titleText(titleText)
+    {
+        if (this._titleText === titleText)
+            return;
+        this._titleText = titleText || "";
+        if (this.titleElement)
+            this.titleElement.textContent = this._titleText;
+    },
+    
+    /**
+     * @return {string}
+     */
+    type: function()
+    {
+        return this._type;
+    },
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseNavigatorTreeElement}
+ * @param {string} type
+ * @param {string} title
+ */
+WebInspector.NavigatorFolderTreeElement = function(type, title)
+{
+    var iconClass = WebInspector.NavigatorView.iconClassForType(type);
+    WebInspector.BaseNavigatorTreeElement.call(this, type, title, [iconClass], true);
+}
+
+WebInspector.NavigatorFolderTreeElement.prototype = {
+    onpopulate: function()
+    {
+        this._node.populate();
+    },
+
+    onattach: function()
+    {
+        WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
+        this.collapse();
+    },
+
+    /**
+     * @param {WebInspector.NavigatorFolderTreeNode} node
+     */
+    setNode: function(node)
+    {
+        this._node = node;
+        var paths = [];
+        while (node && !node.isRoot()) {
+            paths.push(node._title);
+            node = node.parent;
+        }
+        paths.reverse();
+        this.tooltip = paths.join("/");
+    },
+
+    __proto__: WebInspector.BaseNavigatorTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseNavigatorTreeElement}
+ * @param {WebInspector.NavigatorView} navigatorView
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ * @param {string} title
+ */
+WebInspector.NavigatorSourceTreeElement = function(navigatorView, uiSourceCode, title)
+{
+    WebInspector.BaseNavigatorTreeElement.call(this, WebInspector.NavigatorTreeOutline.Types.UISourceCode, title, ["navigator-" + uiSourceCode.contentType().name() + "-tree-item"], false);
+    this._navigatorView = navigatorView;
+    this._uiSourceCode = uiSourceCode;
+    this.tooltip = uiSourceCode.originURL();
+}
+
+WebInspector.NavigatorSourceTreeElement.prototype = {
+    /**
+     * @return {WebInspector.UISourceCode}
+     */
+    get uiSourceCode()
+    {
+        return this._uiSourceCode;
+    },
+
+    onattach: function()
+    {
+        WebInspector.BaseNavigatorTreeElement.prototype.onattach.call(this);
+        this.listItemElement.draggable = true;
+        this.listItemElement.addEventListener("click", this._onclick.bind(this), false);
+        this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), false);
+        this.listItemElement.addEventListener("mousedown", this._onmousedown.bind(this), false);
+        this.listItemElement.addEventListener("dragstart", this._ondragstart.bind(this), false);
+    },
+
+    _onmousedown: function(event)
+    {
+        if (event.which === 1) // Warm-up data for drag'n'drop
+            this._uiSourceCode.requestContent(callback.bind(this));
+        /**
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function callback(content, contentEncoded, mimeType)
+        {
+            this._warmedUpContent = content;
+        }
+    },
+
+    _shouldRenameOnMouseDown: function()
+    {
+        var isSelected = this === this.treeOutline.selectedTreeElement;
+        var isFocused = this.treeOutline.childrenListElement.isSelfOrAncestor(document.activeElement);
+        return isSelected && isFocused && !WebInspector.isBeingEdited(this.treeOutline.element);
+    },
+
+    selectOnMouseDown: function(event)
+    {
+        if (!this._shouldRenameOnMouseDown()) {
+            TreeElement.prototype.selectOnMouseDown.call(this, event);
+            return;
+        }
+        setTimeout(rename.bind(this), 300);
+
+        function rename()
+        {
+            if (this._shouldRenameOnMouseDown())
+                this._navigatorView.handleRename(this._uiSourceCode);
+        }
+    },
+
+    _ondragstart: function(event)
+    {
+        event.dataTransfer.setData("text/plain", this._warmedUpContent);
+        event.dataTransfer.effectAllowed = "copy";
+        return true;
+    },
+
+    onspace: function()
+    {
+        this._navigatorView._scriptSelected(this.uiSourceCode, true);
+        return true;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onclick: function(event)
+    {
+        this._navigatorView._scriptSelected(this.uiSourceCode, false);
+    },
+
+    /**
+     * @param {Event} event
+     */
+    ondblclick: function(event)
+    {
+        var middleClick = event.button === 1;
+        this._navigatorView._scriptSelected(this.uiSourceCode, !middleClick);
+    },
+
+    onenter: function()
+    {
+        this._navigatorView._scriptSelected(this.uiSourceCode, true);
+        return true;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _handleContextMenuEvent: function(event)
+    {
+        this.select();
+        this._navigatorView.handleContextMenu(event, this._uiSourceCode);
+    },
+
+    __proto__: WebInspector.BaseNavigatorTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @param {string} id
+ */
+WebInspector.NavigatorTreeNode = function(id)
+{
+    this.id = id;
+    this._children = {};
+}
+
+WebInspector.NavigatorTreeNode.prototype = {
+    /**
+     * @return {TreeElement}
+     */
+    treeElement: function() { },
+
+    dispose: function() { },
+
+    /**
+     * @return {boolean}
+     */
+    isRoot: function()
+    {
+        return false;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    hasChildren: function()
+    {
+        return true;
+    },
+
+    populate: function()
+    {
+        if (this.isPopulated())
+            return;
+        if (this.parent)
+            this.parent.populate();
+        this._populated = true;
+        this.wasPopulated();
+    },
+
+    wasPopulated: function()
+    {
+        for (var id in this._children)
+            this.treeElement().appendChild(this._children[id].treeElement());
+    },
+
+    didAddChild: function(node)
+    {
+        if (this.isPopulated())
+            this.treeElement().appendChild(node.treeElement());
+    },
+
+    willRemoveChild: function(node)
+    {
+        if (this.isPopulated())
+            this.treeElement().removeChild(node.treeElement());
+    },
+
+    isPopulated: function()
+    {
+        return this._populated;
+    },
+
+    isEmpty: function()
+    {
+        return this.children().length === 0;
+    },
+
+    child: function(id)
+    {
+        return this._children[id];
+    },
+
+    children: function()
+    {
+        return Object.values(this._children);
+    },
+
+    appendChild: function(node)
+    {
+        this._children[node.id] = node;
+        node.parent = this;
+        this.didAddChild(node);
+    },
+
+    removeChild: function(node)
+    {
+        this.willRemoveChild(node);
+        delete this._children[node.id];
+        delete node.parent;
+        node.dispose();
+    },
+
+    reset: function()
+    {
+        this._children = {};
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NavigatorTreeNode}
+ * @param {WebInspector.NavigatorView} navigatorView
+ */
+WebInspector.NavigatorRootTreeNode = function(navigatorView)
+{
+    WebInspector.NavigatorTreeNode.call(this, "");
+    this._navigatorView = navigatorView;
+}
+
+WebInspector.NavigatorRootTreeNode.prototype = {
+    /**
+     * @return {boolean}
+     */
+    isRoot: function()
+    {
+        return true;
+    },
+
+    /**
+     * @return {TreeOutline}
+     */
+    treeElement: function()
+    {
+        return this._navigatorView._scriptsTree;
+    },
+
+    wasPopulated: function()
+    {
+        for (var id in this._children)
+            this.treeElement().appendChild(this._children[id].treeElement());
+    },
+
+    didAddChild: function(node)
+    {
+        if (this.isPopulated())
+            this.treeElement().appendChild(node.treeElement());
+    },
+
+    willRemoveChild: function(node)
+    {
+        if (this.isPopulated())
+            this.treeElement().removeChild(node.treeElement());
+    },
+
+    __proto__: WebInspector.NavigatorTreeNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NavigatorTreeNode}
+ * @param {WebInspector.NavigatorView} navigatorView
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ */
+WebInspector.NavigatorUISourceCodeTreeNode = function(navigatorView, uiSourceCode)
+{
+    WebInspector.NavigatorTreeNode.call(this, uiSourceCode.name());
+    this._navigatorView = navigatorView;
+    this._uiSourceCode = uiSourceCode;
+}
+
+WebInspector.NavigatorUISourceCodeTreeNode.prototype = {
+    /**
+     * @return {WebInspector.UISourceCode}
+     */
+    uiSourceCode: function()
+    {
+        return this._uiSourceCode;
+    },
+
+    /**
+     * @return {TreeElement}
+     */
+    treeElement: function()
+    {
+        if (this._treeElement)
+            return this._treeElement;
+
+        this._treeElement = new WebInspector.NavigatorSourceTreeElement(this._navigatorView, this._uiSourceCode, "");
+        this.updateTitle();
+
+        this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._titleChanged, this);
+        this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
+        this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+        this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._formattedChanged, this);
+
+        return this._treeElement;
+    },
+
+    /**
+     * @param {boolean=} ignoreIsDirty
+     */
+    updateTitle: function(ignoreIsDirty)
+    {
+        if (!this._treeElement)
+            return;
+
+        var titleText = this._uiSourceCode.name().trimEnd(100);
+        if (!titleText)
+            titleText = WebInspector.UIString("(program)");
+        if (!ignoreIsDirty && this._uiSourceCode.isDirty())
+            titleText = "*" + titleText;
+        this._treeElement.titleText = titleText;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    hasChildren: function()
+    {
+        return false;
+    },
+
+    dispose: function()
+    {
+        if (!this._treeElement)
+            return;
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._titleChanged, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._formattedChanged, this);
+    },
+
+    _titleChanged: function(event)
+    {
+        this.updateTitle();
+    },
+
+    _workingCopyChanged: function(event)
+    {
+        this.updateTitle();
+    },
+
+    _workingCopyCommitted: function(event)
+    {
+        this.updateTitle();
+    },
+
+    _formattedChanged: function(event)
+    {
+        this.updateTitle();
+    },
+
+    /**
+     * @param {boolean=} select
+     */
+    reveal: function(select)
+    {
+        this.parent.populate();
+        this.parent.treeElement().expand();
+        this._treeElement.reveal();
+        if (select)
+            this._treeElement.select();
+    },
+
+    /**
+     * @param {function(boolean)=} callback
+     */
+    rename: function(callback)
+    {
+        if (!this._treeElement)
+            return;
+
+        // Tree outline should be marked as edited as well as the tree element to prevent search from starting.
+        var treeOutlineElement = this._treeElement.treeOutline.element;
+        WebInspector.markBeingEdited(treeOutlineElement, true);
+
+        function commitHandler(element, newTitle, oldTitle)
+        {
+            if (newTitle && newTitle !== oldTitle)
+                this._navigatorView._fileRenamed(this._uiSourceCode, newTitle);
+            afterEditing.call(this, true);
+        }
+
+        function cancelHandler()
+        {
+            afterEditing.call(this, false);
+        }
+
+        /**
+         * @param {boolean} committed
+         */
+        function afterEditing(committed)
+        {
+            WebInspector.markBeingEdited(treeOutlineElement, false);
+            this.updateTitle();
+            if (callback)
+                callback(committed);
+        }
+
+        var editingConfig = new WebInspector.EditingConfig(commitHandler.bind(this), cancelHandler.bind(this));
+        this.updateTitle(true);
+        WebInspector.startEditing(this._treeElement.titleElement, editingConfig);
+        window.getSelection().setBaseAndExtent(this._treeElement.titleElement, 0, this._treeElement.titleElement, 1);
+    },
+
+    __proto__: WebInspector.NavigatorTreeNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NavigatorTreeNode}
+ * @param {WebInspector.NavigatorView} navigatorView
+ * @param {string} id
+ * @param {string} type
+ * @param {string} title
+ */
+WebInspector.NavigatorFolderTreeNode = function(navigatorView, id, type, title)
+{
+    WebInspector.NavigatorTreeNode.call(this, id);
+    this._navigatorView = navigatorView;
+    this._type = type;
+    this._title = title;
+}
+
+WebInspector.NavigatorFolderTreeNode.prototype = {
+    /**
+     * @return {TreeElement}
+     */
+    treeElement: function()
+    {
+        if (this._treeElement)
+            return this._treeElement;
+        this._treeElement = this._createTreeElement(this._title, this);
+        return this._treeElement;
+    },
+
+    /**
+     * @return {TreeElement}
+     */
+    _createTreeElement: function(title, node)
+    {
+        var treeElement = new WebInspector.NavigatorFolderTreeElement(this._type, title);
+        treeElement.setNode(node);
+        return treeElement;
+    },
+
+    wasPopulated: function()
+    {
+        if (!this._treeElement || this._treeElement._node !== this)
+            return;
+        this._addChildrenRecursive();
+    },
+
+    _addChildrenRecursive: function()
+    {
+        for (var id in this._children) {
+            var child = this._children[id];
+            this.didAddChild(child);
+            if (child instanceof WebInspector.NavigatorFolderTreeNode)
+                child._addChildrenRecursive();
+        }
+    },
+
+    _shouldMerge: function(node)
+    {
+        return this._type !== WebInspector.NavigatorTreeOutline.Types.Domain && node instanceof WebInspector.NavigatorFolderTreeNode;
+    },
+
+    didAddChild: function(node)
+    {
+        function titleForNode(node)
+        {
+            return node._title;
+        }
+
+        if (!this._treeElement)
+            return;
+
+        var children = this.children();
+
+        if (children.length === 1 && this._shouldMerge(node)) {
+            node._isMerged = true;
+            this._treeElement.titleText = this._treeElement.titleText + "/" + node._title;
+            node._treeElement = this._treeElement;
+            this._treeElement.setNode(node);
+            return;
+        }
+
+        var oldNode;
+        if (children.length === 2)
+            oldNode = children[0] !== node ? children[0] : children[1];
+        if (oldNode && oldNode._isMerged) {
+            delete oldNode._isMerged;
+            var mergedToNodes = [];
+            mergedToNodes.push(this);
+            var treeNode = this;
+            while (treeNode._isMerged) {
+                treeNode = treeNode.parent;
+                mergedToNodes.push(treeNode);
+            }
+            mergedToNodes.reverse();
+            var titleText = mergedToNodes.map(titleForNode).join("/");
+
+            var nodes = [];
+            treeNode = oldNode;
+            do {
+                nodes.push(treeNode);
+                children = treeNode.children();
+                treeNode = children.length === 1 ? children[0] : null;
+            } while (treeNode && treeNode._isMerged);
+
+            if (!this.isPopulated()) {
+                this._treeElement.titleText = titleText;
+                this._treeElement.setNode(this);
+                for (var i = 0; i < nodes.length; ++i) {
+                    delete nodes[i]._treeElement;
+                    delete nodes[i]._isMerged;
+                }
+                return;
+            }
+            var oldTreeElement = this._treeElement;
+            var treeElement = this._createTreeElement(titleText, this);
+            for (var i = 0; i < mergedToNodes.length; ++i)
+                mergedToNodes[i]._treeElement = treeElement;
+            oldTreeElement.parent.appendChild(treeElement);
+
+            oldTreeElement.setNode(nodes[nodes.length - 1]);
+            oldTreeElement.titleText = nodes.map(titleForNode).join("/");
+            oldTreeElement.parent.removeChild(oldTreeElement);
+            this._treeElement.appendChild(oldTreeElement);
+            if (oldTreeElement.expanded)
+                treeElement.expand();
+        }
+        if (this.isPopulated())
+            this._treeElement.appendChild(node.treeElement());
+    },
+
+    willRemoveChild: function(node)
+    {
+        if (node._isMerged || !this.isPopulated())
+            return;
+        this._treeElement.removeChild(node._treeElement);
+    },
+
+    __proto__: WebInspector.NavigatorTreeNode.prototype
+}
diff --git a/Source/devtools/front_end/NetworkItemView.js b/Source/devtools/front_end/NetworkItemView.js
new file mode 100644
index 0000000..0dc783b
--- /dev/null
+++ b/Source/devtools/front_end/NetworkItemView.js
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.TabbedPane}
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.NetworkItemView = function(request)
+{
+    WebInspector.TabbedPane.call(this);
+    this.element.addStyleClass("network-item-view");
+
+    var headersView = new WebInspector.RequestHeadersView(request);
+    this.appendTab("headers", WebInspector.UIString("Headers"), headersView);
+
+    this.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
+
+    if (request.type === WebInspector.resourceTypes.WebSocket) {
+        var frameView = new WebInspector.ResourceWebSocketFrameView(request);
+        this.appendTab("webSocketFrames", WebInspector.UIString("Frames"), frameView);
+    } else {
+        var responseView = new WebInspector.RequestResponseView(request);
+        var previewView = new WebInspector.RequestPreviewView(request, responseView);
+        this.appendTab("preview", WebInspector.UIString("Preview"), previewView);
+        this.appendTab("response", WebInspector.UIString("Response"), responseView);
+    }
+
+    if (request.requestCookies || request.responseCookies) {
+        this._cookiesView = new WebInspector.RequestCookiesView(request);
+        this.appendTab("cookies", WebInspector.UIString("Cookies"), this._cookiesView);
+    }
+
+    if (request.timing) {
+        var timingView = new WebInspector.RequestTimingView(request);
+        this.appendTab("timing", WebInspector.UIString("Timing"), timingView);
+    }
+    this._request = request;
+}
+
+WebInspector.NetworkItemView.prototype = {
+    wasShown: function()
+    {
+        WebInspector.TabbedPane.prototype.wasShown.call(this);
+        this._selectTab();
+    },
+
+    /**
+     * @param {string=} tabId
+     */
+    _selectTab: function(tabId)
+    {
+        if (!tabId)
+            tabId = WebInspector.settings.resourceViewTab.get();
+
+        if (!this.selectTab(tabId)) {
+            this._isInFallbackSelection = true;
+            this.selectTab("headers");
+            delete this._isInFallbackSelection;
+        }
+    },
+
+    _tabSelected: function(event)
+    {
+        if (!event.data.isUserGesture)
+            return;
+
+        WebInspector.settings.resourceViewTab.set(event.data.tabId);
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.NetworkRequestTabSelected,
+            tab: event.data.tabId,
+            url: this._request.url
+        });
+    },
+
+    /**
+      * @return {WebInspector.NetworkRequest}
+      */
+    request: function()
+    {
+        return this._request;
+    },
+
+    __proto__: WebInspector.TabbedPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.RequestView}
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestContentView = function(request)
+{
+    WebInspector.RequestView.call(this, request);
+}
+
+WebInspector.RequestContentView.prototype = {
+    hasContent: function()
+    {
+        return true;
+    },
+
+    get innerView()
+    {
+        return this._innerView;
+    },
+
+    set innerView(innerView)
+    {
+        this._innerView = innerView;
+    },
+
+    wasShown: function()
+    {
+        this._ensureInnerViewShown();
+    },
+
+    _ensureInnerViewShown: function()
+    {
+        if (this._innerViewShowRequested)
+            return;
+        this._innerViewShowRequested = true;
+
+        /**
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function callback(content, contentEncoded, mimeType)
+        {
+            this._innerViewShowRequested = false;
+            this.contentLoaded();
+        }
+
+        this.request.requestContent(callback.bind(this));
+    },
+
+    contentLoaded: function()
+    {
+        // Should be implemented by subclasses.
+    },
+
+    canHighlightLine: function()
+    {
+        return this._innerView && this._innerView.canHighlightLine();
+    },
+
+    highlightLine: function(line)
+    {
+        if (this.canHighlightLine())
+            this._innerView.highlightLine(line);
+    },
+
+    __proto__: WebInspector.RequestView.prototype
+}
diff --git a/Source/devtools/front_end/NetworkLog.js b/Source/devtools/front_end/NetworkLog.js
new file mode 100644
index 0000000..fc45856
--- /dev/null
+++ b/Source/devtools/front_end/NetworkLog.js
@@ -0,0 +1,150 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.NetworkLog = function()
+{
+    this._requests = [];
+    this._requestForId = {};
+    WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._onMainFrameNavigated, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.OnLoad, this._onLoad, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._onDOMContentLoaded, this);
+}
+
+WebInspector.NetworkLog.prototype = {
+    /**
+     * @return {Array.<WebInspector.NetworkRequest>}
+     */
+    get requests()
+    {
+        return this._requests;
+    },
+
+    /**
+     * @param {string} url
+     * @return {WebInspector.NetworkRequest}
+     */
+    requestForURL: function(url)
+    {
+        for (var i = 0; i < this._requests.length; ++i) {
+            if (this._requests[i].url === url)
+                return this._requests[i];
+        }
+        return null;
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} request
+     * @return {WebInspector.PageLoad}
+     */
+    pageLoadForRequest: function(request)
+    {
+        return request.__page;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onMainFrameNavigated: function(event)
+    {
+        var mainFrame = /** type {WebInspector.ResourceTreeFrame} */ event.data;
+        // Preserve requests from the new session.
+        this._currentPageLoad = null;
+        var oldRequests = this._requests.splice(0, this._requests.length);
+        for (var i = 0; i < oldRequests.length; ++i) {
+            var request = oldRequests[i];
+            if (request.loaderId === mainFrame.loaderId) {
+                if (!this._currentPageLoad)
+                    this._currentPageLoad = new WebInspector.PageLoad(request);
+                this._requests.push(request);
+                request.__page = this._currentPageLoad;
+            }
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onRequestStarted: function(event)
+    {
+        var request = /** @type {WebInspector.NetworkRequest} */ (event.data);
+        this._requests.push(request);
+        this._requestForId[request.requestId] = request;
+        request.__page = this._currentPageLoad;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onDOMContentLoaded: function(event)
+    {
+        if (this._currentPageLoad)
+            this._currentPageLoad.contentLoadTime = event.data;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onLoad: function(event)
+    {
+        if (this._currentPageLoad)
+            this._currentPageLoad.loadTime = event.data;
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @return {?WebInspector.NetworkRequest}
+     */
+    requestForId: function(requestId)
+    {
+        return this._requestForId[requestId];
+    }
+}
+
+/**
+ * @type {WebInspector.NetworkLog}
+ */
+WebInspector.networkLog = null;
+
+/**
+ * @constructor
+ * @param {WebInspector.NetworkRequest} mainRequest
+ */
+WebInspector.PageLoad = function(mainRequest)
+{
+    this.id = ++WebInspector.PageLoad._lastIdentifier;
+    this.url = mainRequest.url;
+    this.startTime = mainRequest.startTime;
+}
+
+WebInspector.PageLoad._lastIdentifier = 0;
diff --git a/Source/devtools/front_end/NetworkManager.js b/Source/devtools/front_end/NetworkManager.js
new file mode 100644
index 0000000..c88333f
--- /dev/null
+++ b/Source/devtools/front_end/NetworkManager.js
@@ -0,0 +1,584 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.NetworkManager = function()
+{
+    WebInspector.Object.call(this);
+    this._dispatcher = new WebInspector.NetworkDispatcher(this);
+    if (WebInspector.settings.cacheDisabled.get())
+        NetworkAgent.setCacheDisabled(true);
+
+    NetworkAgent.enable();
+
+    WebInspector.settings.cacheDisabled.addChangeListener(this._cacheDisabledSettingChanged, this);
+}
+
+WebInspector.NetworkManager.EventTypes = {
+    RequestStarted: "RequestStarted",
+    RequestUpdated: "RequestUpdated",
+    RequestFinished: "RequestFinished",
+    RequestUpdateDropped: "RequestUpdateDropped"
+}
+
+WebInspector.NetworkManager._MIMETypes = {
+    "text/html":                   {"document": true},
+    "text/xml":                    {"document": true},
+    "text/plain":                  {"document": true},
+    "application/xhtml+xml":       {"document": true},
+    "text/css":                    {"stylesheet": true},
+    "text/xsl":                    {"stylesheet": true},
+    "image/jpg":                   {"image": true},
+    "image/jpeg":                  {"image": true},
+    "image/pjpeg":                 {"image": true},
+    "image/png":                   {"image": true},
+    "image/gif":                   {"image": true},
+    "image/bmp":                   {"image": true},
+    "image/svg+xml":               {"image": true, "font": true, "document": true},
+    "image/vnd.microsoft.icon":    {"image": true},
+    "image/webp":                  {"image": true},
+    "image/x-icon":                {"image": true},
+    "image/x-xbitmap":             {"image": true},
+    "font/ttf":                    {"font": true},
+    "font/opentype":               {"font": true},
+    "application/font-woff":       {"font": true},
+    "application/x-font-type1":    {"font": true},
+    "application/x-font-ttf":      {"font": true},
+    "application/x-truetype-font": {"font": true},
+    "text/javascript":             {"script": true},
+    "text/ecmascript":             {"script": true},
+    "application/javascript":      {"script": true},
+    "application/ecmascript":      {"script": true},
+    "application/x-javascript":    {"script": true},
+    "application/json":            {"script": true},
+    "text/javascript1.1":          {"script": true},
+    "text/javascript1.2":          {"script": true},
+    "text/javascript1.3":          {"script": true},
+    "text/jscript":                {"script": true},
+    "text/livescript":             {"script": true},
+}
+
+WebInspector.NetworkManager.prototype = {
+    /**
+     * @param {string} url
+     * @return {WebInspector.NetworkRequest}
+     */
+    inflightRequestForURL: function(url)
+    {
+        return this._dispatcher._inflightRequestsByURL[url];
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _cacheDisabledSettingChanged: function(event)
+    {
+        var enabled = /** @type {boolean} */ (event.data);
+        NetworkAgent.setCacheDisabled(enabled);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {NetworkAgent.Dispatcher}
+ */
+WebInspector.NetworkDispatcher = function(manager)
+{
+    this._manager = manager;
+    this._inflightRequestsById = {};
+    this._inflightRequestsByURL = {};
+    InspectorBackend.registerNetworkDispatcher(this);
+}
+
+WebInspector.NetworkDispatcher.prototype = {
+    /**
+     * @param {NetworkAgent.Headers} headersMap
+     * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
+     */
+    _headersMapToHeadersArray: function(headersMap)
+    {
+        var result = [];
+        for (var name in headersMap) {
+            var values = headersMap[name].split("\n");
+            for (var i = 0; i < values.length; ++i)
+                result.push({name: name, value: values[i]});
+        }
+        return result;
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} networkRequest
+     * @param {NetworkAgent.Request} request
+     */
+    _updateNetworkRequestWithRequest: function(networkRequest, request)
+    {
+        networkRequest.requestMethod = request.method;
+        networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers);
+        networkRequest.requestFormData = request.postData;
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} networkRequest
+     * @param {NetworkAgent.Response=} response
+     */
+    _updateNetworkRequestWithResponse: function(networkRequest, response)
+    {
+        if (!response)
+            return;
+
+        if (response.url && networkRequest.url !== response.url)
+            networkRequest.url = response.url;
+        networkRequest.mimeType = response.mimeType;
+        networkRequest.statusCode = response.status;
+        networkRequest.statusText = response.statusText;
+        networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
+        if (response.headersText)
+            networkRequest.responseHeadersText = response.headersText;
+        if (response.requestHeaders)
+            networkRequest.requestHeaders = this._headersMapToHeadersArray(response.requestHeaders);
+        if (response.requestHeadersText)
+            networkRequest.requestHeadersText = response.requestHeadersText;
+
+        networkRequest.connectionReused = response.connectionReused;
+        networkRequest.connectionId = response.connectionId;
+
+        if (response.fromDiskCache)
+            networkRequest.cached = true;
+        else
+            networkRequest.timing = response.timing;
+
+        if (!this._mimeTypeIsConsistentWithType(networkRequest)) {
+            WebInspector.console.addMessage(WebInspector.ConsoleMessage.create(WebInspector.ConsoleMessage.MessageSource.Network,
+                WebInspector.ConsoleMessage.MessageLevel.Log,
+                WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s: \"%s\".", networkRequest.type.title(), networkRequest.mimeType, networkRequest.url),
+                WebInspector.ConsoleMessage.MessageType.Log,
+                "",
+                0,
+                1,
+                [],
+                null,
+                networkRequest.requestId));
+        }
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} networkRequest
+     * @return {boolean}
+     */
+    _mimeTypeIsConsistentWithType: function(networkRequest)
+    {
+        // If status is an error, content is likely to be of an inconsistent type,
+        // as it's going to be an error message. We do not want to emit a warning
+        // for this, though, as this will already be reported as resource loading failure.
+        // Also, if a URL like http://localhost/wiki/load.php?debug=true&lang=en produces text/css and gets reloaded,
+        // it is 304 Not Modified and its guessed mime-type is text/php, which is wrong.
+        // Don't check for mime-types in 304-resources.
+        if (networkRequest.hasErrorStatusCode() || networkRequest.statusCode === 304 || networkRequest.statusCode === 204)
+            return true;
+
+        if (typeof networkRequest.type === "undefined"
+            || networkRequest.type === WebInspector.resourceTypes.Other
+            || networkRequest.type === WebInspector.resourceTypes.XHR
+            || networkRequest.type === WebInspector.resourceTypes.WebSocket)
+            return true;
+
+        if (!networkRequest.mimeType)
+            return true; // Might be not known for cached resources with null responses.
+
+        if (networkRequest.mimeType in WebInspector.NetworkManager._MIMETypes)
+            return networkRequest.type.name() in WebInspector.NetworkManager._MIMETypes[networkRequest.mimeType];
+
+        return false;
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} networkRequest
+     * @param {?NetworkAgent.CachedResource} cachedResource
+     */
+    _updateNetworkRequestWithCachedResource: function(networkRequest, cachedResource)
+    {
+        networkRequest.type = WebInspector.resourceTypes[cachedResource.type];
+        networkRequest.resourceSize = cachedResource.bodySize;
+        this._updateNetworkRequestWithResponse(networkRequest, cachedResource.response);
+    },
+
+    /**
+     * @param {NetworkAgent.Response} response
+     * @return {boolean}
+     */
+    _isNull: function(response)
+    {
+        if (!response)
+            return true;
+        return !response.status && !response.mimeType && (!response.headers || !Object.keys(response.headers).length);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.FrameId} frameId
+     * @param {NetworkAgent.LoaderId} loaderId
+     * @param {string} documentURL
+     * @param {NetworkAgent.Request} request
+     * @param {NetworkAgent.Timestamp} time
+     * @param {NetworkAgent.Initiator} initiator
+     * @param {NetworkAgent.Response=} redirectResponse
+     */
+    requestWillBeSent: function(requestId, frameId, loaderId, documentURL, request, time, initiator, redirectResponse)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (networkRequest) {
+            // FIXME: move this check to the backend.
+            if (!redirectResponse)
+                return;
+            this.responseReceived(requestId, frameId, loaderId, time, PageAgent.ResourceType.Other, redirectResponse);
+            networkRequest = this._appendRedirect(requestId, time, request.url);
+        } else
+            networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, request.url, documentURL, initiator);
+        networkRequest.hasNetworkData = true;
+        this._updateNetworkRequestWithRequest(networkRequest, request);
+        networkRequest.startTime = time;
+
+        this._startNetworkRequest(networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     */
+    requestServedFromCache: function(requestId)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+
+        networkRequest.cached = true;
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.FrameId} frameId
+     * @param {NetworkAgent.LoaderId} loaderId
+     * @param {NetworkAgent.Timestamp} time
+     * @param {PageAgent.ResourceType} resourceType
+     * @param {NetworkAgent.Response} response
+     */
+    responseReceived: function(requestId, frameId, loaderId, time, resourceType, response)
+    {
+        // FIXME: move this check to the backend.
+        if (this._isNull(response))
+            return;
+
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest) {
+            // We missed the requestWillBeSent.
+            var eventData = {};
+            eventData.url = response.url;
+            eventData.frameId = frameId;
+            eventData.loaderId = loaderId;
+            eventData.resourceType = resourceType;
+            eventData.mimeType = response.mimeType;
+            this._manager.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdateDropped, eventData);
+            return;
+        }
+
+        networkRequest.responseReceivedTime = time;
+        networkRequest.type = WebInspector.resourceTypes[resourceType];
+
+        this._updateNetworkRequestWithResponse(networkRequest, response);
+
+        this._updateNetworkRequest(networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} time
+     * @param {number} dataLength
+     * @param {number} encodedDataLength
+     */
+    dataReceived: function(requestId, time, dataLength, encodedDataLength)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+
+        networkRequest.resourceSize += dataLength;
+        if (encodedDataLength != -1)
+            networkRequest.increaseTransferSize(encodedDataLength);
+        networkRequest.endTime = time;
+
+        this._updateNetworkRequest(networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} finishTime
+     */
+    loadingFinished: function(requestId, finishTime)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+        this._finishNetworkRequest(networkRequest, finishTime);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} time
+     * @param {string} localizedDescription
+     * @param {boolean=} canceled
+     */
+    loadingFailed: function(requestId, time, localizedDescription, canceled)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+
+        networkRequest.failed = true;
+        networkRequest.canceled = canceled;
+        networkRequest.localizedFailDescription = localizedDescription;
+        this._finishNetworkRequest(networkRequest, time);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.FrameId} frameId
+     * @param {NetworkAgent.LoaderId} loaderId
+     * @param {string} documentURL
+     * @param {NetworkAgent.Timestamp} time
+     * @param {NetworkAgent.Initiator} initiator
+     * @param {NetworkAgent.CachedResource} cachedResource
+     */
+    requestServedFromMemoryCache: function(requestId, frameId, loaderId, documentURL, time, initiator, cachedResource)
+    {
+        var networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, cachedResource.url, documentURL, initiator);
+        this._updateNetworkRequestWithCachedResource(networkRequest, cachedResource);
+        networkRequest.cached = true;
+        networkRequest.requestMethod = "GET";
+        this._startNetworkRequest(networkRequest);
+        networkRequest.startTime = networkRequest.responseReceivedTime = time;
+        this._finishNetworkRequest(networkRequest, time);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {string} requestURL
+     */
+    webSocketCreated: function(requestId, requestURL)
+    {
+        var networkRequest = new WebInspector.NetworkRequest(requestId, requestURL, "", "", "");
+        networkRequest.type = WebInspector.resourceTypes.WebSocket;
+        this._startNetworkRequest(networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} time
+     * @param {NetworkAgent.WebSocketRequest} request
+     */
+    webSocketWillSendHandshakeRequest: function(requestId, time, request)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+
+        networkRequest.requestMethod = "GET";
+        networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers);
+        networkRequest.startTime = time;
+
+        this._updateNetworkRequest(networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} time
+     * @param {NetworkAgent.WebSocketResponse} response
+     */
+    webSocketHandshakeResponseReceived: function(requestId, time, response)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+
+        networkRequest.statusCode = response.status;
+        networkRequest.statusText = response.statusText;
+        networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
+        networkRequest.responseReceivedTime = time;
+
+        this._updateNetworkRequest(networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} time
+     * @param {NetworkAgent.WebSocketFrame} response
+     */
+    webSocketFrameReceived: function(requestId, time, response)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+
+        networkRequest.addFrame(response, time);
+        networkRequest.responseReceivedTime = time;
+
+        this._updateNetworkRequest(networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} time
+     * @param {NetworkAgent.WebSocketFrame} response
+     */
+    webSocketFrameSent: function(requestId, time, response)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+
+        networkRequest.addFrame(response, time, true);
+        networkRequest.responseReceivedTime = time;
+
+        this._updateNetworkRequest(networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} time
+     * @param {string} errorMessage
+     */
+    webSocketFrameError: function(requestId, time, errorMessage)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+
+        networkRequest.addFrameError(errorMessage, time);
+        networkRequest.responseReceivedTime = time;
+
+        this._updateNetworkRequest(networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} time
+     */
+    webSocketClosed: function(requestId, time)
+    {
+        var networkRequest = this._inflightRequestsById[requestId];
+        if (!networkRequest)
+            return;
+        this._finishNetworkRequest(networkRequest, time);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {NetworkAgent.Timestamp} time
+     * @param {string} redirectURL
+     * @return {WebInspector.NetworkRequest}
+     */
+    _appendRedirect: function(requestId, time, redirectURL)
+    {
+        var originalNetworkRequest = this._inflightRequestsById[requestId];
+        var previousRedirects = originalNetworkRequest.redirects || [];
+        originalNetworkRequest.requestId = "redirected:" + requestId + "." + previousRedirects.length;
+        delete originalNetworkRequest.redirects;
+        if (previousRedirects.length > 0)
+            originalNetworkRequest.redirectSource = previousRedirects[previousRedirects.length - 1];
+        this._finishNetworkRequest(originalNetworkRequest, time);
+        var newNetworkRequest = this._createNetworkRequest(requestId, originalNetworkRequest.frameId, originalNetworkRequest.loaderId,
+             redirectURL, originalNetworkRequest.documentURL, originalNetworkRequest.initiator);
+        newNetworkRequest.redirects = previousRedirects.concat(originalNetworkRequest);
+        return newNetworkRequest;
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} networkRequest
+     */
+    _startNetworkRequest: function(networkRequest)
+    {
+        this._inflightRequestsById[networkRequest.requestId] = networkRequest;
+        this._inflightRequestsByURL[networkRequest.url] = networkRequest;
+        this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestStarted, networkRequest);
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} networkRequest
+     */
+    _updateNetworkRequest: function(networkRequest)
+    {
+        this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdated, networkRequest);
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} networkRequest
+     * @param {NetworkAgent.Timestamp} finishTime
+     */
+    _finishNetworkRequest: function(networkRequest, finishTime)
+    {
+        networkRequest.endTime = finishTime;
+        networkRequest.finished = true;
+        this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestFinished, networkRequest);
+        delete this._inflightRequestsById[networkRequest.requestId];
+        delete this._inflightRequestsByURL[networkRequest.url];
+    },
+
+    /**
+     * @param {string} eventType
+     * @param {WebInspector.NetworkRequest} networkRequest
+     */
+    _dispatchEventToListeners: function(eventType, networkRequest)
+    {
+        this._manager.dispatchEventToListeners(eventType, networkRequest);
+    },
+
+    /**
+     * @param {NetworkAgent.RequestId} requestId
+     * @param {string} frameId
+     * @param {NetworkAgent.LoaderId} loaderId
+     * @param {string} url
+     * @param {string} documentURL
+     * @param {NetworkAgent.Initiator} initiator
+     */
+    _createNetworkRequest: function(requestId, frameId, loaderId, url, documentURL, initiator)
+    {
+        var networkRequest = new WebInspector.NetworkRequest(requestId, url, documentURL, frameId, loaderId);
+        networkRequest.initiator = initiator;
+        return networkRequest;
+    }
+}
+
+/**
+ * @type {?WebInspector.NetworkManager}
+ */
+WebInspector.networkManager = null;
diff --git a/Source/devtools/front_end/NetworkPanel.js b/Source/devtools/front_end/NetworkPanel.js
new file mode 100644
index 0000000..ec1c4ba
--- /dev/null
+++ b/Source/devtools/front_end/NetworkPanel.js
@@ -0,0 +1,2488 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
+ * 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+importScript("RequestView.js");
+importScript("NetworkItemView.js");
+importScript("RequestCookiesView.js");
+importScript("RequestHeadersView.js");
+importScript("RequestHTMLView.js");
+importScript("RequestJSONView.js");
+importScript("RequestPreviewView.js");
+importScript("RequestResponseView.js");
+importScript("RequestTimingView.js");
+importScript("ResourceWebSocketFrameView.js");
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.Setting} coulmnsVisibilitySetting
+ */
+WebInspector.NetworkLogView = function(coulmnsVisibilitySetting)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("networkLogView.css");
+
+    this._coulmnsVisibilitySetting = coulmnsVisibilitySetting;
+    this._allowRequestSelection = false;
+    this._requests = [];
+    this._requestsById = {};
+    this._requestsByURL = {};
+    this._staleRequests = {};
+    this._requestGridNodes = {};
+    this._lastRequestGridNodeId = 0;
+    this._mainRequestLoadTime = -1;
+    this._mainRequestDOMContentTime = -1;
+    this._typeFilterElements = {};
+    this._typeFilter = WebInspector.NetworkLogView._trivialTypeFilter;
+    this._matchedRequests = [];
+    this._highlightedSubstringChanges = [];
+    this._filteredOutRequests = new Map();
+
+    this._matchedRequestsMap = {};
+    this._currentMatchedRequestIndex = -1;
+
+    this._createStatusbarButtons();
+    this._createStatusBarItems();
+    this._linkifier = new WebInspector.Linkifier();
+
+    WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestStarted, this._onRequestStarted, this);
+    WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestUpdated, this._onRequestUpdated, this);
+    WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestUpdated, this);
+
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.OnLoad, this._onLoadEventFired, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, this._domContentLoadedEventFired, this);
+
+    this._initializeView();
+
+    WebInspector.networkLog.requests.forEach(this._appendRequest.bind(this));
+}
+
+WebInspector.NetworkLogView.HTTPSchemas = {"http": true, "https": true, "ws": true, "wss": true};
+WebInspector.NetworkLogView._defaultColumnsVisivility = {method: true, status: true, domain: false, type: true, initiator: true, cookies: false, setCookies: false, size: true, time: true};
+WebInspector.NetworkLogView._defaultRefreshDelay = 500;
+WebInspector.NetworkLogView.ALL_TYPES = "all";
+
+WebInspector.NetworkLogView.prototype = {
+    _initializeView: function()
+    {
+        this.element.id = "network-container";
+
+        this._createSortingFunctions();
+        this._createTable();
+        this._createTimelineGrid();
+        this._createSummaryBar();
+
+        if (!this.useLargeRows)
+            this._setLargerRequests(this.useLargeRows);
+
+        this._allowPopover = true;
+        this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this));
+        // Enable faster hint.
+        this._popoverHelper.setTimeout(100);
+
+        this.calculator = new WebInspector.NetworkTransferTimeCalculator();
+        this._toggleTypeFilter(WebInspector.NetworkLogView.ALL_TYPES, false);
+
+        this.switchToDetailedView();
+    },
+
+    get statusBarItems()
+    {
+        return [this._largerRequestsButton.element, this._preserveLogToggle.element, this._clearButton.element, this._filterBarElement, this._progressBarContainer];
+    },
+
+    get useLargeRows()
+    {
+        return WebInspector.settings.resourcesLargeRows.get();
+    },
+
+    set allowPopover(flag)
+    {
+        this._allowPopover = flag;
+    },
+
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        if (!this._dataGrid) // Not initialized yet.
+            return [];
+        return [this._dataGrid.scrollContainer];
+    },
+
+    onResize: function()
+    {
+        this._updateOffscreenRows();
+    },
+
+    _createTimelineGrid: function()
+    {
+        this._timelineGrid = new WebInspector.TimelineGrid();
+        this._timelineGrid.element.addStyleClass("network-timeline-grid");
+        this._dataGrid.element.appendChild(this._timelineGrid.element);
+    },
+
+    _createTable: function()
+    {
+        var columns = [];
+        columns.push({
+            id: "name", 
+            titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path")),
+            title: WebInspector.UIString("Name"),
+            sortable: true,
+            weight: 20,
+            disclosure: true
+        });
+
+        columns.push({
+            id: "method",
+            title: WebInspector.UIString("Method"),
+            sortable: true,
+            weight: 6
+        });
+
+        columns.push({
+            id: "status",
+            titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text")),
+            title: WebInspector.UIString("Status"),
+            sortable: true,
+            weight: 6
+        });
+
+        columns.push({
+            id: "domain",
+            title: WebInspector.UIString("Domain"),
+            sortable: true,
+            weight: 6
+        });
+
+        columns.push({
+            id: "type",
+            title: WebInspector.UIString("Type"),
+            sortable: true,
+            weight: 6
+        });
+
+        columns.push({
+            id: "initiator",
+            title: WebInspector.UIString("Initiator"),
+            sortable: true,
+            weight: 10
+        });
+
+        columns.push({
+            id: "cookies",
+            title: WebInspector.UIString("Cookies"),
+            sortable: true,
+            weight: 6,
+            align: WebInspector.DataGrid.Align.Right
+        });
+
+        columns.push({
+            id: "setCookies",
+            title: WebInspector.UIString("Set-Cookies"),
+            sortable: true,
+            weight: 6,
+            align: WebInspector.DataGrid.Align.Right
+        });
+
+        columns.push({
+            id: "size",
+            titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Content")),
+            title: WebInspector.UIString("Size"),
+            sortable: true,
+            weight: 6,
+            align: WebInspector.DataGrid.Align.Right
+        });
+
+        columns.push({
+            id: "time",
+            titleDOMFragment: this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Latency")),
+            title: WebInspector.UIString("Time"),
+            sortable: true,
+            weight: 6,
+            align: WebInspector.DataGrid.Align.Right
+        });
+
+        columns.push({
+            id: "timeline",
+            titleDOMFragment: document.createDocumentFragment(),
+            title: WebInspector.UIString("Timeline"),
+            sortable: false,
+            weight: 40,
+            sort: WebInspector.DataGrid.Order.Ascending
+        });
+
+        this._dataGrid = new WebInspector.DataGrid(columns);
+        this._dataGrid.setName("networkLog");
+        this._dataGrid.resizeMethod = WebInspector.DataGrid.ResizeMethod.Last;
+        this._dataGrid.element.addStyleClass("network-log-grid");
+        this._dataGrid.element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
+        this._dataGrid.show(this.element);
+
+        // Event listeners need to be added _after_ we attach to the document, so that owner document is properly update.
+        this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortItems, this);
+        this._dataGrid.addEventListener(WebInspector.DataGrid.Events.ColumnsResized, this._updateDividersIfNeeded, this);
+        this._dataGrid.scrollContainer.addEventListener("scroll", this._updateOffscreenRows.bind(this));
+
+        this._patchTimelineHeader();
+    },
+
+    _makeHeaderFragment: function(title, subtitle)
+    {
+        var fragment = document.createDocumentFragment();
+        fragment.createTextChild(title);
+        var subtitleDiv = fragment.createChild("div", "network-header-subtitle");
+        subtitleDiv.createTextChild(subtitle);
+        return fragment;
+    },
+
+    _patchTimelineHeader: function()
+    {
+        var timelineSorting = document.createElement("select");
+
+        var option = document.createElement("option");
+        option.value = "startTime";
+        option.label = WebInspector.UIString("Timeline");
+        timelineSorting.appendChild(option);
+
+        option = document.createElement("option");
+        option.value = "startTime";
+        option.label = WebInspector.UIString("Start Time");
+        timelineSorting.appendChild(option);
+
+        option = document.createElement("option");
+        option.value = "responseTime";
+        option.label = WebInspector.UIString("Response Time");
+        timelineSorting.appendChild(option);
+
+        option = document.createElement("option");
+        option.value = "endTime";
+        option.label = WebInspector.UIString("End Time");
+        timelineSorting.appendChild(option);
+
+        option = document.createElement("option");
+        option.value = "duration";
+        option.label = WebInspector.UIString("Duration");
+        timelineSorting.appendChild(option);
+
+        option = document.createElement("option");
+        option.value = "latency";
+        option.label = WebInspector.UIString("Latency");
+        timelineSorting.appendChild(option);
+
+        var header = this._dataGrid.headerTableHeader("timeline");
+        header.replaceChild(timelineSorting, header.firstChild);
+
+        timelineSorting.addEventListener("click", function(event) { event.consume() }, false);
+        timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false);
+        this._timelineSortSelector = timelineSorting;
+    },
+
+    _createSortingFunctions: function()
+    {
+        this._sortingFunctions = {};
+        this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator;
+        this._sortingFunctions.method = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "method", false);
+        this._sortingFunctions.status = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "statusCode", false);
+        this._sortingFunctions.domain = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "domain", false);
+        this._sortingFunctions.type = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "mimeType", false);
+        this._sortingFunctions.initiator = WebInspector.NetworkDataGridNode.InitiatorComparator;
+        this._sortingFunctions.cookies = WebInspector.NetworkDataGridNode.RequestCookiesCountComparator;
+        this._sortingFunctions.setCookies = WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator;
+        this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator;
+        this._sortingFunctions.time = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", false);
+        this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
+        this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime", false);
+        this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "endTime", false);
+        this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "responseReceivedTime", false);
+        this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "duration", true);
+        this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "latency", true);
+
+        var timeCalculator = new WebInspector.NetworkTransferTimeCalculator();
+        var durationCalculator = new WebInspector.NetworkTransferDurationCalculator();
+
+        this._calculators = {};
+        this._calculators.timeline = timeCalculator;
+        this._calculators.startTime = timeCalculator;
+        this._calculators.endTime = timeCalculator;
+        this._calculators.responseTime = timeCalculator;
+        this._calculators.duration = durationCalculator;
+        this._calculators.latency = durationCalculator;
+    },
+
+    _sortItems: function()
+    {
+        this._removeAllNodeHighlights();
+        var columnIdentifier = this._dataGrid.sortColumnIdentifier();
+        if (columnIdentifier === "timeline") {
+            this._sortByTimeline();
+            return;
+        }
+        var sortingFunction = this._sortingFunctions[columnIdentifier];
+        if (!sortingFunction)
+            return;
+
+        this._dataGrid.sortNodes(sortingFunction, !this._dataGrid.isSortOrderAscending());
+        this._timelineSortSelector.selectedIndex = 0;
+        this._updateOffscreenRows();
+
+        this.searchCanceled();
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.NetworkSort,
+            column: columnIdentifier,
+            sortOrder: this._dataGrid.sortOrder()
+        });
+    },
+
+    _sortByTimeline: function()
+    {
+        this._removeAllNodeHighlights();
+        var selectedIndex = this._timelineSortSelector.selectedIndex;
+        if (!selectedIndex)
+            selectedIndex = 1; // Sort by start time by default.
+        var selectedOption = this._timelineSortSelector[selectedIndex];
+        var value = selectedOption.value;
+
+        var sortingFunction = this._sortingFunctions[value];
+        this._dataGrid.sortNodes(sortingFunction);
+        this.calculator = this._calculators[value];
+        if (this.calculator.startAtZero)
+            this._timelineGrid.hideEventDividers();
+        else
+            this._timelineGrid.showEventDividers();
+        this._dataGrid.markColumnAsSortedBy("timeline", WebInspector.DataGrid.Order.Ascending);
+        this._updateOffscreenRows();
+    },
+
+    /**
+     * @param {string} typeName
+     * @param {string} label
+     */
+    _addTypeFilter: function(typeName, label)
+    {
+        var typeFilterElement = this._filterBarElement.createChild("li", typeName);
+        typeFilterElement.typeName = typeName;
+        typeFilterElement.createTextChild(label);
+        typeFilterElement.addEventListener("click", this._onTypeFilterClicked.bind(this), false);
+        this._typeFilterElements[typeName] = typeFilterElement;
+    },
+
+    _createStatusBarItems: function()
+    {
+        var filterBarElement = document.createElement("div");
+        filterBarElement.className = "scope-bar status-bar-item";
+        filterBarElement.title = WebInspector.UIString("Use %s Click to select multiple types.", WebInspector.KeyboardShortcut.shortcutToString("", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta));
+        this._filterBarElement = filterBarElement;
+
+        this._addTypeFilter(WebInspector.NetworkLogView.ALL_TYPES, WebInspector.UIString("All"));
+        filterBarElement.createChild("div", "scope-bar-divider");
+
+        for (var typeId in WebInspector.resourceTypes) {
+            var type = WebInspector.resourceTypes[typeId];
+            this._addTypeFilter(type.name(), type.categoryTitle());
+        }
+
+        this._progressBarContainer = document.createElement("div");
+        this._progressBarContainer.className = "status-bar-item";
+    },
+
+    _createSummaryBar: function()
+    {
+        var tbody = this._dataGrid.dataTableBody;
+        var tfoot = document.createElement("tfoot");
+        var tr = tfoot.createChild("tr", "revealed network-summary-bar");
+        var td = tr.createChild("td");
+        td.setAttribute("colspan", 7);
+        tbody.parentNode.insertBefore(tfoot, tbody);
+        this._summaryBarElement = td;
+    },
+
+    _updateSummaryBar: function()
+    {
+        var requestsNumber = this._requests.length;
+
+        if (!requestsNumber) {
+            if (this._summaryBarElement._isDisplayingWarning)
+                return;
+            this._summaryBarElement._isDisplayingWarning = true;
+
+            this._summaryBarElement.createChild("div", "warning-icon-small");
+            this._summaryBarElement.appendChild(document.createTextNode(
+                WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity.")));
+            return;
+        }
+        delete this._summaryBarElement._isDisplayingWarning;
+
+        var transferSize = 0;
+        var selectedRequestsNumber = 0;
+        var selectedTransferSize = 0;
+        var baseTime = -1;
+        var maxTime = -1;
+        for (var i = 0; i < this._requests.length; ++i) {
+            var request = this._requests[i];
+            var requestTransferSize = (request.cached || !request.transferSize) ? 0 : request.transferSize;
+            transferSize += requestTransferSize;
+            if (!this._filteredOutRequests.get(request)) {
+                selectedRequestsNumber++;
+                selectedTransferSize += requestTransferSize;
+            }
+            if (request.url === WebInspector.inspectedPageURL)
+                baseTime = request.startTime;
+            if (request.endTime > maxTime)
+                maxTime = request.endTime;
+        }
+        var text = "";
+        if (selectedRequestsNumber !== requestsNumber) {
+            text += String.sprintf(WebInspector.UIString("%d / %d requests"), selectedRequestsNumber, requestsNumber);
+            text += "  \u2758  " + String.sprintf(WebInspector.UIString("%s / %s transferred"), Number.bytesToString(selectedTransferSize), Number.bytesToString(transferSize));
+        } else {
+            text += String.sprintf(WebInspector.UIString("%d requests"), requestsNumber);
+            text += "  \u2758  " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize));
+        }
+        if (baseTime !== -1 && this._mainRequestLoadTime !== -1 && this._mainRequestDOMContentTime !== -1 && this._mainRequestDOMContentTime > baseTime) {
+            text += "  \u2758  " + String.sprintf(WebInspector.UIString("%s (onload: %s, DOMContentLoaded: %s)"),
+                        Number.secondsToString(maxTime - baseTime),
+                        Number.secondsToString(this._mainRequestLoadTime - baseTime),
+                        Number.secondsToString(this._mainRequestDOMContentTime - baseTime));
+        }
+        this._summaryBarElement.textContent = text;
+    },
+
+    /**
+     * @param {!Event} e
+     */
+    _onTypeFilterClicked: function(e)
+    {
+        var toggle;
+        if (WebInspector.isMac())
+            toggle = e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey;
+        else
+            toggle = e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey;
+
+        this._toggleTypeFilter(e.target.typeName, toggle);
+
+        this._removeAllNodeHighlights();
+        this.searchCanceled();
+        this._filterRequests();
+    },
+
+    /**
+     * @param {string} typeName
+     * @param {boolean} allowMultiSelect
+     */
+    _toggleTypeFilter: function(typeName, allowMultiSelect)
+    {
+        if (allowMultiSelect && typeName !== WebInspector.NetworkLogView.ALL_TYPES)
+            this._typeFilterElements[WebInspector.NetworkLogView.ALL_TYPES].removeStyleClass("selected");
+        else {
+            for (var key in this._typeFilterElements)
+                this._typeFilterElements[key].removeStyleClass("selected");
+        }
+
+        var filterElement = this._typeFilterElements[typeName];
+        filterElement.enableStyleClass("selected", !filterElement.hasStyleClass("selected"));
+
+        var allowedTypes = {};
+        for (var key in this._typeFilterElements) {
+            if (this._typeFilterElements[key].hasStyleClass("selected"))
+                allowedTypes[key] = true;
+        }
+
+        if (typeName === WebInspector.NetworkLogView.ALL_TYPES)
+            this._typeFilter = WebInspector.NetworkLogView._trivialTypeFilter;
+        else
+            this._typeFilter = WebInspector.NetworkLogView._typeFilter.bind(null, allowedTypes);
+    },
+
+    _scheduleRefresh: function()
+    {
+        if (this._needsRefresh)
+            return;
+
+        this._needsRefresh = true;
+
+        if (this.isShowing() && !this._refreshTimeout)
+            this._refreshTimeout = setTimeout(this.refresh.bind(this), WebInspector.NetworkLogView._defaultRefreshDelay);
+    },
+
+    _updateDividersIfNeeded: function()
+    {
+        if (!this._dataGrid)
+            return;
+        var timelineColumn = this._dataGrid.columns.timeline;
+        for (var i = 0; i < this._dataGrid.resizers.length; ++i) {
+            if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnIndex) {
+                // Position timline grid location.
+                this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left;
+            }
+        }
+
+        var proceed = true;
+        if (!this.isShowing()) {
+            this._scheduleRefresh();
+            proceed = false;
+        } else {
+            this.calculator.setDisplayWindow(this._timelineGrid.dividersElement.clientWidth);
+            proceed = this._timelineGrid.updateDividers(this.calculator);
+        }
+        if (!proceed)
+            return;
+
+        if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) {
+            // If our current sorting method starts at zero, that means it shows all
+            // requests starting at the same point, and so onLoad event and DOMContent
+            // event lines really wouldn't make much sense here, so don't render them.
+            // Additionally, if the calculator doesn't have the computePercentageFromEventTime
+            // function defined, we are probably sorting by size, and event times aren't relevant
+            // in this case.
+            return;
+        }
+
+        this._timelineGrid.removeEventDividers();
+        if (this._mainRequestLoadTime !== -1) {
+            var percent = this.calculator.computePercentageFromEventTime(this._mainRequestLoadTime);
+
+            var loadDivider = document.createElement("div");
+            loadDivider.className = "network-event-divider network-red-divider";
+
+            var loadDividerPadding = document.createElement("div");
+            loadDividerPadding.className = "network-event-divider-padding";
+            loadDividerPadding.title = WebInspector.UIString("Load event fired");
+            loadDividerPadding.appendChild(loadDivider);
+            loadDividerPadding.style.left = percent + "%";
+            this._timelineGrid.addEventDivider(loadDividerPadding);
+        }
+
+        if (this._mainRequestDOMContentTime !== -1) {
+            var percent = this.calculator.computePercentageFromEventTime(this._mainRequestDOMContentTime);
+
+            var domContentDivider = document.createElement("div");
+            domContentDivider.className = "network-event-divider network-blue-divider";
+
+            var domContentDividerPadding = document.createElement("div");
+            domContentDividerPadding.className = "network-event-divider-padding";
+            domContentDividerPadding.title = WebInspector.UIString("DOMContent event fired");
+            domContentDividerPadding.appendChild(domContentDivider);
+            domContentDividerPadding.style.left = percent + "%";
+            this._timelineGrid.addEventDivider(domContentDividerPadding);
+        }
+    },
+
+    _refreshIfNeeded: function()
+    {
+        if (this._needsRefresh)
+            this.refresh();
+    },
+
+    _invalidateAllItems: function()
+    {
+        for (var i = 0; i < this._requests.length; ++i) {
+            var request = this._requests[i];
+            this._staleRequests[request.requestId] = request;
+        }
+    },
+
+    get calculator()
+    {
+        return this._calculator;
+    },
+
+    set calculator(x)
+    {
+        if (!x || this._calculator === x)
+            return;
+
+        this._calculator = x;
+        this._calculator.reset();
+
+        this._invalidateAllItems();
+        this.refresh();
+    },
+
+    _requestGridNode: function(request)
+    {
+        return this._requestGridNodes[request.__gridNodeId];
+    },
+
+    _createRequestGridNode: function(request)
+    {
+        var node = new WebInspector.NetworkDataGridNode(this, request);
+        request.__gridNodeId = this._lastRequestGridNodeId++;
+        this._requestGridNodes[request.__gridNodeId] = node;
+        return node;
+    },
+
+    _createStatusbarButtons: function()
+    {
+        this._preserveLogToggle = new WebInspector.StatusBarButton(WebInspector.UIString("Preserve Log upon Navigation"), "record-profile-status-bar-item");
+        this._preserveLogToggle.addEventListener("click", this._onPreserveLogClicked, this);
+
+        this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
+        this._clearButton.addEventListener("click", this._reset, this);
+
+        this._largerRequestsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item");
+        this._largerRequestsButton.toggled = WebInspector.settings.resourcesLargeRows.get();
+        this._largerRequestsButton.addEventListener("click", this._toggleLargerRequests, this);
+    },
+
+    _onLoadEventFired: function(event)
+    {
+        this._mainRequestLoadTime = event.data || -1;
+        // Schedule refresh to update boundaries and draw the new line.
+        this._scheduleRefresh();
+    },
+
+    _domContentLoadedEventFired: function(event)
+    {
+        this._mainRequestDOMContentTime = event.data || -1;
+        // Schedule refresh to update boundaries and draw the new line.
+        this._scheduleRefresh();
+    },
+
+    wasShown: function()
+    {
+        this._refreshIfNeeded();
+    },
+
+    willHide: function()
+    {
+        this._popoverHelper.hidePopover();
+    },
+
+    refresh: function()
+    {
+        this._needsRefresh = false;
+        if (this._refreshTimeout) {
+            clearTimeout(this._refreshTimeout);
+            delete this._refreshTimeout;
+        }
+
+        this._removeAllNodeHighlights();
+        var wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
+        var boundariesChanged = false;
+        if (this.calculator.updateBoundariesForEventTime) {
+            boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestLoadTime) || boundariesChanged;
+            boundariesChanged = this.calculator.updateBoundariesForEventTime(this._mainRequestDOMContentTime) || boundariesChanged;
+        }
+
+        for (var requestId in this._staleRequests) {
+            var request = this._staleRequests[requestId];
+            var node = this._requestGridNode(request);
+            if (node)
+                node.refreshRequest();
+            else {
+                // Create the timeline tree element and graph.
+                node = this._createRequestGridNode(request);
+                this._dataGrid.rootNode().appendChild(node);
+                node.refreshRequest();
+                this._applyFilter(node);
+            }
+
+            if (this.calculator.updateBoundaries(request))
+                boundariesChanged = true;
+
+            if (!node.isFilteredOut())
+                this._updateHighlightIfMatched(request);
+        }
+
+        if (boundariesChanged) {
+            // The boundaries changed, so all item graphs are stale.
+            this._invalidateAllItems();
+        }
+
+        for (var requestId in this._staleRequests)
+            this._requestGridNode(this._staleRequests[requestId]).refreshGraph(this.calculator);
+
+        this._staleRequests = {};
+        this._sortItems();
+        this._updateSummaryBar();
+        this._dataGrid.updateWidths();
+        // FIXME: evaluate performance impact of moving this before a call to sortItems()
+        if (wasScrolledToLastRow)
+            this._dataGrid.scrollToLastRow();
+    },
+
+    _onPreserveLogClicked: function(e)
+    {
+        this._preserveLogToggle.toggled = !this._preserveLogToggle.toggled;
+    },
+
+    _reset: function()
+    {
+        this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.ViewCleared);
+
+        this._clearSearchMatchedList();
+        if (this._popoverHelper)
+            this._popoverHelper.hidePopover();
+
+        if (this._calculator)
+            this._calculator.reset();
+
+        this._requests = [];
+        this._requestsById = {};
+        this._requestsByURL = {};
+        this._staleRequests = {};
+        this._requestGridNodes = {};
+
+        if (this._dataGrid) {
+            this._dataGrid.rootNode().removeChildren();
+            this._updateDividersIfNeeded();
+            this._updateSummaryBar();
+        }
+
+        this._mainRequestLoadTime = -1;
+        this._mainRequestDOMContentTime = -1;
+    },
+
+    get requests()
+    {
+        return this._requests;
+    },
+
+    requestById: function(id)
+    {
+        return this._requestsById[id];
+    },
+
+    _onRequestStarted: function(event)
+    {
+        this._appendRequest(event.data);
+    },
+
+    _appendRequest: function(request)
+    {
+        this._requests.push(request);
+
+        // In case of redirect request id is reassigned to a redirected
+        // request and we need to update _requestsById and search results.
+        if (this._requestsById[request.requestId]) {
+            var oldRequest = request.redirects[request.redirects.length - 1];
+            this._requestsById[oldRequest.requestId] = oldRequest;
+
+            this._updateSearchMatchedListAfterRequestIdChanged(request.requestId, oldRequest.requestId);
+        }
+        this._requestsById[request.requestId] = request;
+
+        this._requestsByURL[request.url] = request;
+
+        // Pull all the redirects of the main request upon commit load.
+        if (request.redirects) {
+            for (var i = 0; i < request.redirects.length; ++i)
+                this._refreshRequest(request.redirects[i]);
+        }
+
+        this._refreshRequest(request);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onRequestUpdated: function(event)
+    {
+        var request = /** @type {WebInspector.NetworkRequest} */ (event.data);
+        this._refreshRequest(request);
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} request
+     */
+    _refreshRequest: function(request)
+    {
+        this._staleRequests[request.requestId] = request;
+        this._scheduleRefresh();
+    },
+
+    clear: function()
+    {
+        if (this._preserveLogToggle.toggled)
+            return;
+        this._reset();
+    },
+
+    _mainFrameNavigated: function(event)
+    {
+        if (this._preserveLogToggle.toggled)
+            return;
+
+        var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data);
+        var loaderId = frame.loaderId;
+
+        // Preserve provisional load requests.
+        var requestsToPreserve = [];
+        for (var i = 0; i < this._requests.length; ++i) {
+            var request = this._requests[i];
+            if (request.loaderId === loaderId)
+                requestsToPreserve.push(request);
+        }
+
+        this._reset();
+
+        // Restore preserved items.
+        for (var i = 0; i < requestsToPreserve.length; ++i)
+            this._appendRequest(requestsToPreserve[i]);
+    },
+
+    switchToDetailedView: function()
+    {
+        if (!this._dataGrid)
+            return;
+        if (this._dataGrid.selectedNode)
+            this._dataGrid.selectedNode.selected = false;
+
+        this.element.removeStyleClass("brief-mode");
+        this._detailedMode = true;
+        this._updateColumns();
+    },
+
+    switchToBriefView: function()
+    {
+        this.element.addStyleClass("brief-mode");
+        this._removeAllNodeHighlights();
+        this._detailedMode = false;
+        this._updateColumns();
+        this._popoverHelper.hidePopover();
+    },
+
+    _toggleLargerRequests: function()
+    {
+        WebInspector.settings.resourcesLargeRows.set(!WebInspector.settings.resourcesLargeRows.get());
+        this._setLargerRequests(WebInspector.settings.resourcesLargeRows.get());
+    },
+
+    _setLargerRequests: function(enabled)
+    {
+        this._largerRequestsButton.toggled = enabled;
+        if (!enabled) {
+            this._largerRequestsButton.title = WebInspector.UIString("Use large resource rows.");
+            this._dataGrid.element.addStyleClass("small");
+            this._timelineGrid.element.addStyleClass("small");
+        } else {
+            this._largerRequestsButton.title = WebInspector.UIString("Use small resource rows.");
+            this._dataGrid.element.removeStyleClass("small");
+            this._timelineGrid.element.removeStyleClass("small");
+        }
+        this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, { largeRows: enabled });
+        this._updateOffscreenRows();
+    },
+
+    _getPopoverAnchor: function(element)
+    {
+        if (!this._allowPopover)
+            return;
+        var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label");
+        if (anchor && anchor.parentElement.request && anchor.parentElement.request.timing)
+            return anchor;
+        anchor = element.enclosingNodeOrSelfWithClass("network-script-initiated");
+        if (anchor && anchor.request && anchor.request.initiator)
+            return anchor;
+
+        return null;
+    },
+
+    /**
+     * @param {Element} anchor
+     * @param {WebInspector.Popover} popover
+     */
+    _showPopover: function(anchor, popover)
+    {
+        var content;
+        if (anchor.hasStyleClass("network-script-initiated"))
+            content = this._generateScriptInitiatedPopoverContent(anchor.request);
+        else
+            content = WebInspector.RequestTimingView.createTimingTable(anchor.parentElement.request);
+        popover.show(content, anchor);
+    },
+
+    _onHidePopover: function()
+    {
+        this._linkifier.reset();
+    },
+
+    /**
+     * @param {!WebInspector.NetworkRequest} request
+     * @return {!Element}
+     */
+    _generateScriptInitiatedPopoverContent: function(request)
+    {
+        var stackTrace = request.initiator.stackTrace;
+        var framesTable = document.createElement("table");
+        for (var i = 0; i < stackTrace.length; ++i) {
+            var stackFrame = stackTrace[i];
+            var row = document.createElement("tr");
+            row.createChild("td").textContent = stackFrame.functionName ? stackFrame.functionName : WebInspector.UIString("(anonymous function)");
+            row.createChild("td").textContent = " @ ";
+            row.createChild("td").appendChild(this._linkifier.linkifyLocation(stackFrame.url, stackFrame.lineNumber - 1, stackFrame.columnNumber - 1));
+            framesTable.appendChild(row);
+        }
+        return framesTable;
+    },
+
+    _updateColumns: function()
+    {
+        var columnsVisibility = this._coulmnsVisibilitySetting.get();
+        var detailedMode = !!this._detailedMode;
+        for (var columnIdentifier in columnsVisibility) {
+            var visible = detailedMode && columnsVisibility[columnIdentifier];
+            this._dataGrid.setColumnVisible(columnIdentifier, visible);
+        }
+        this._dataGrid.setColumnVisible("timeline", detailedMode);
+        this._dataGrid.applyColumnWeights();
+    },
+
+    /**
+     * @param {string} columnIdentifier
+     */
+    _toggleColumnVisibility: function(columnIdentifier)
+    {
+        var columnsVisibility = this._coulmnsVisibilitySetting.get();
+        columnsVisibility[columnIdentifier] = !columnsVisibility[columnIdentifier];
+        this._coulmnsVisibilitySetting.set(columnsVisibility);
+
+        this._updateColumns();
+    },
+
+    /**
+     * @return {!Array.<string>}
+     */
+    _getConfigurableColumnIDs: function()
+    {
+        if (this._configurableColumnIDs)
+            return this._configurableColumnIDs;
+
+        var columns = this._dataGrid.columns;
+        function compare(id1, id2)
+        {
+            return columns[id1].title.compareTo(columns[id2].title);
+        }
+
+        var columnIDs = Object.keys(this._coulmnsVisibilitySetting.get());
+        this._configurableColumnIDs = columnIDs.sort(compare);
+        return this._configurableColumnIDs;
+    },
+
+    _contextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+
+        if (this._detailedMode && event.target.isSelfOrDescendant(this._dataGrid.headerTableBody)) {
+            var columnsVisibility = this._coulmnsVisibilitySetting.get();
+            var columnIDs = this._getConfigurableColumnIDs();
+            for (var i = 0; i < columnIDs.length; ++i) {
+                var columnIdentifier = columnIDs[i];
+                var column = this._dataGrid.columns[columnIdentifier];
+                contextMenu.appendCheckboxItem(column.title, this._toggleColumnVisibility.bind(this, columnIdentifier), !!columnsVisibility[columnIdentifier]);
+            }
+            contextMenu.show();
+            return;
+        }
+
+        var gridNode = this._dataGrid.dataGridNodeFromNode(event.target);
+        var request = gridNode && gridNode._request;
+
+        if (request) {
+            contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), WebInspector.openResource.bind(WebInspector, request.url, false));
+            contextMenu.appendSeparator();
+            contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), this._copyLocation.bind(this, request));
+            if (request.requestHeadersText)
+                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy request headers" : "Copy Request Headers"), this._copyRequestHeaders.bind(this, request));
+            if (request.responseHeadersText)
+                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy response headers" : "Copy Response Headers"), this._copyResponseHeaders.bind(this, request));
+            contextMenu.appendItem(WebInspector.UIString("Copy as cURL"), this._copyCurlCommand.bind(this, request));
+        }
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy all as HAR" : "Copy All as HAR"), this._copyAll.bind(this));
+
+        contextMenu.appendSeparator();
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save as HAR with content" : "Save as HAR with Content"), this._exportAll.bind(this));
+
+        contextMenu.appendSeparator();
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cache" : "Clear Browser Cache"), this._clearBrowserCache.bind(this));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear browser cookies" : "Clear Browser Cookies"), this._clearBrowserCookies.bind(this));
+
+        if (request && request.type === WebInspector.resourceTypes.XHR) {
+            contextMenu.appendSeparator();
+            contextMenu.appendItem(WebInspector.UIString("Replay XHR"), this._replayXHR.bind(this, request.requestId));
+            contextMenu.appendSeparator();
+        }
+
+        contextMenu.show();
+    },
+
+    _replayXHR: function(requestId)
+    {
+        NetworkAgent.replayXHR(requestId);
+    },
+
+    _copyAll: function()
+    {
+        var harArchive = {
+            log: (new WebInspector.HARLog(this._requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter))).build()
+        };
+        InspectorFrontendHost.copyText(JSON.stringify(harArchive, null, 2));
+    },
+
+    _copyLocation: function(request)
+    {
+        InspectorFrontendHost.copyText(request.url);
+    },
+
+    _copyRequestHeaders: function(request)
+    {
+        InspectorFrontendHost.copyText(request.requestHeadersText);
+    },
+
+    _copyResponseHeaders: function(request)
+    {
+        InspectorFrontendHost.copyText(request.responseHeadersText);
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} request
+     */
+    _copyCurlCommand: function(request)
+    {
+        InspectorFrontendHost.copyText(this._generateCurlCommand(request));
+    },
+
+    _exportAll: function()
+    {
+        var filename = WebInspector.inspectedPageDomain + ".har";
+        var stream = new WebInspector.FileOutputStream();
+        stream.open(filename, openCallback.bind(this));
+        function openCallback()
+        {
+            var progressIndicator = new WebInspector.ProgressIndicator();
+            this._progressBarContainer.appendChild(progressIndicator.element);
+            var harWriter = new WebInspector.HARWriter();
+            harWriter.write(stream, this._requests.filter(WebInspector.NetworkLogView.HTTPRequestsFilter), progressIndicator);
+        }
+    },
+
+    _clearBrowserCache: function()
+    {
+        if (confirm(WebInspector.UIString("Are you sure you want to clear browser cache?")))
+            NetworkAgent.clearBrowserCache();
+    },
+
+    _clearBrowserCookies: function()
+    {
+        if (confirm(WebInspector.UIString("Are you sure you want to clear browser cookies?")))
+            NetworkAgent.clearBrowserCookies();
+    },
+
+    _updateOffscreenRows: function()
+    {
+        var dataTableBody = this._dataGrid.dataTableBody;
+        var rows = dataTableBody.children;
+        var recordsCount = rows.length;
+        if (recordsCount < 2)
+            return;  // Filler row only.
+
+        var visibleTop = this._dataGrid.scrollContainer.scrollTop;
+        var visibleBottom = visibleTop + this._dataGrid.scrollContainer.offsetHeight;
+
+        var rowHeight = 0;
+
+        // Filler is at recordsCount - 1.
+        var unfilteredRowIndex = 0;
+        for (var i = 0; i < recordsCount - 1; ++i) {
+            var row = rows[i];
+
+            var dataGridNode = this._dataGrid.dataGridNodeFromNode(row);
+            if (dataGridNode.isFilteredOut()) {
+                row.removeStyleClass("offscreen");
+                continue;
+            }
+
+            if (!rowHeight)
+                rowHeight = row.offsetHeight;
+
+            var rowIsVisible = unfilteredRowIndex * rowHeight < visibleBottom && (unfilteredRowIndex + 1) * rowHeight > visibleTop;
+            if (rowIsVisible !== row.rowIsVisible) {
+                row.enableStyleClass("offscreen", !rowIsVisible);
+                row.rowIsVisible = rowIsVisible;
+            }
+            unfilteredRowIndex++;
+        }
+    },
+
+    _matchRequest: function(request)
+    {
+        if (!this._searchRegExp)
+            return -1;
+
+        if (!request.name().match(this._searchRegExp) && !request.path().match(this._searchRegExp))
+            return -1;
+
+        if (request.requestId in this._matchedRequestsMap)
+            return this._matchedRequestsMap[request.requestId];
+
+        var matchedRequestIndex = this._matchedRequests.length;
+        this._matchedRequestsMap[request.requestId] = matchedRequestIndex;
+        this._matchedRequests.push(request.requestId);
+
+        return matchedRequestIndex;
+    },
+
+    _clearSearchMatchedList: function()
+    {
+        delete this._searchRegExp;
+        this._matchedRequests = [];
+        this._matchedRequestsMap = {};
+        this._removeAllHighlights();
+    },
+
+    _updateSearchMatchedListAfterRequestIdChanged: function(oldRequestId, newRequestId)
+    {
+        var requestIndex = this._matchedRequestsMap[oldRequestId];
+        if (requestIndex) {
+            delete this._matchedRequestsMap[oldRequestId];
+            this._matchedRequestsMap[newRequestId] = requestIndex;
+            this._matchedRequests[requestIndex] = newRequestId;
+        }
+    },
+
+    _updateHighlightIfMatched: function(request)
+    {
+        var matchedRequestIndex = this._matchRequest(request);
+        if (matchedRequestIndex === -1)
+            return;
+
+        this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
+
+        if (this._currentMatchedRequestIndex !== -1 && this._currentMatchedRequestIndex !== matchedRequestIndex)
+            return;
+
+        this._highlightNthMatchedRequestForSearch(matchedRequestIndex, false);
+    },
+
+    _removeAllHighlights: function()
+    {
+        for (var i = 0; i < this._highlightedSubstringChanges.length; ++i)
+            WebInspector.revertDomChanges(this._highlightedSubstringChanges[i]);
+        this._highlightedSubstringChanges = [];
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} request
+     * @param {boolean} reveal
+     * @param {RegExp=} regExp
+     */
+    _highlightMatchedRequest: function(request, reveal, regExp)
+    {
+        var node = this._requestGridNode(request);
+        if (!node)
+            return;
+
+        var nameMatched = request.name().match(regExp);
+        var pathMatched = request.path().match(regExp);
+        if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled)
+            this._toggleLargerRequests();
+        var highlightedSubstringChanges = node._highlightMatchedSubstring(regExp);
+        this._highlightedSubstringChanges.push(highlightedSubstringChanges);
+        if (reveal)
+            node.reveal();
+    },
+
+    /**
+     * @param {number} matchedRequestIndex
+     * @param {boolean} reveal
+     */
+    _highlightNthMatchedRequestForSearch: function(matchedRequestIndex, reveal)
+    {
+        var request = this.requestById(this._matchedRequests[matchedRequestIndex]);
+        if (!request)
+            return;
+        this._removeAllHighlights();
+        this._highlightMatchedRequest(request, reveal, this._searchRegExp);
+        var node = this._requestGridNode(request);
+        if (node)
+            this._currentMatchedRequestIndex = matchedRequestIndex;
+
+        this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._currentMatchedRequestIndex);
+    },
+
+    performSearch: function(searchQuery)
+    {
+        var newMatchedRequestIndex = 0;
+        var currentMatchedRequestId;
+        if (this._currentMatchedRequestIndex !== -1)
+            currentMatchedRequestId = this._matchedRequests[this._currentMatchedRequestIndex];
+
+        this._clearSearchMatchedList();
+        this._searchRegExp = createPlainTextSearchRegex(searchQuery, "i");
+
+        var childNodes = this._dataGrid.dataTableBody.childNodes;
+        var requestNodes = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1); // drop the filler row.
+
+        for (var i = 0; i < requestNodes.length; ++i) {
+            var dataGridNode = this._dataGrid.dataGridNodeFromNode(requestNodes[i]);
+            if (dataGridNode.isFilteredOut())
+                continue;
+            if (this._matchRequest(dataGridNode._request) !== -1 && dataGridNode._request.requestId === currentMatchedRequestId)
+                newMatchedRequestIndex = this._matchedRequests.length - 1;
+        }
+
+        this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._matchedRequests.length);
+        this._highlightNthMatchedRequestForSearch(newMatchedRequestIndex, false);
+    },
+
+    /**
+     * @param {!WebInspector.NetworkDataGridNode} node
+     */
+    _applyFilter: function(node)
+    {
+        var filter = this._filterRegExp;
+        var request = node._request;
+        var matches = false;
+        if (this._typeFilter(request)) {
+            matches = !filter || filter.test(request.name()) || filter.test(request.path());
+            if (filter && matches)
+                this._highlightMatchedRequest(request, false, filter);
+        }
+        node.element.enableStyleClass("filtered-out", !matches);
+        if (!matches)
+            this._filteredOutRequests.put(request, true);
+    },
+
+    /**
+     * @param {string} query
+     */
+    performFilter: function(query)
+    {
+        delete this._filterRegExp;
+        if (query)
+            this._filterRegExp = createPlainTextSearchRegex(query, "i");
+        this._filterRequests();
+    },
+
+    _filterRequests: function()
+    {
+        this._removeAllHighlights();
+        this._filteredOutRequests.clear();
+
+        var nodes = this._dataGrid.rootNode().children;
+        for (var i = 0; i < nodes.length; ++i)
+            this._applyFilter(nodes[i]);
+        this._updateSummaryBar();
+        this._updateOffscreenRows();
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        if (!this._matchedRequests.length)
+            return;
+        this._highlightNthMatchedRequestForSearch((this._currentMatchedRequestIndex + this._matchedRequests.length - 1) % this._matchedRequests.length, true);
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        if (!this._matchedRequests.length)
+            return;
+        this._highlightNthMatchedRequestForSearch((this._currentMatchedRequestIndex + 1) % this._matchedRequests.length, true);
+    },
+
+    searchCanceled: function()
+    {
+        this._clearSearchMatchedList();
+        this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, 0);
+    },
+
+    revealAndHighlightRequest: function(request)
+    {
+        this._removeAllNodeHighlights();
+
+        var node = this._requestGridNode(request);
+        if (node) {
+            this._dataGrid.element.focus();
+            node.reveal();
+            this._highlightNode(node);
+        }
+    },
+
+    _removeAllNodeHighlights: function()
+    {
+        if (this._highlightedNode) {
+            this._highlightedNode.element.removeStyleClass("highlighted-row");
+            delete this._highlightedNode;
+        }
+    },
+
+    _highlightNode: function(node)
+    {
+        node.element.addStyleClass("highlighted-row");
+        this._highlightedNode = node;
+    },
+
+   /**
+     * @param {WebInspector.NetworkRequest} request
+     * @return {string}
+     */
+    _generateCurlCommand: function(request)
+    {
+        var command = ["curl"];
+        var ignoredHeaders = {};
+
+        function escapeCharacter(x)
+        {
+           var code = x.charCodeAt(0);
+           if (code < 256) {
+             // Add leading zero when needed to not care about the next character.
+             return code < 16 ? "\\x0" + code.toString(16) : "\\x" + code.toString(16);
+           }
+           code = code.toString(16);
+           return "\\u" + ("0000" + code).substr(code.length, 4);
+        }
+
+        function escape(str)
+        {
+            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 + "'";
+            }
+        }
+        command.push(escape(request.url));
+
+        var inferredMethod = "GET";
+        var data = [];
+        var requestContentType = request.requestContentType();
+        if (requestContentType && requestContentType.startsWith("application/x-www-form-urlencoded") && request.requestFormData) {
+           data.push("--data");
+           data.push(escape(request.requestFormData));
+           ignoredHeaders["Content-Length"] = true;
+           inferredMethod = "POST";
+        } else if (request.requestFormData) {
+           data.push("--data-binary");
+           data.push(escape(request.requestFormData));
+           ignoredHeaders["Content-Length"] = true;
+           inferredMethod = "POST";
+        }
+
+        if (request.requestMethod !== inferredMethod) {
+            command.push("-X");
+            command.push(request.requestMethod);
+        }
+
+        for (var i = 0; i < request.requestHeaders.length; i++) {
+            var header = request.requestHeaders[i];
+            if (header.name in ignoredHeaders)
+                continue;
+            command.push("-H");
+            command.push(escape(header.name + ": " + header.value));
+        }
+        command = command.concat(data);
+        command.push("--compressed");
+        return command.join(" ");
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @param {!WebInspector.NetworkRequest} request
+ * @return {boolean}
+ */
+WebInspector.NetworkLogView.HTTPRequestsFilter = function(request)
+{
+    return request.parsedURL.isValid && (request.parsedURL.scheme in WebInspector.NetworkLogView.HTTPSchemas);
+}
+
+
+WebInspector.NetworkLogView.EventTypes = {
+    ViewCleared: "ViewCleared",
+    RowSizeChanged: "RowSizeChanged",
+    RequestSelected: "RequestSelected",
+    SearchCountUpdated: "SearchCountUpdated",
+    SearchIndexUpdated: "SearchIndexUpdated"
+};
+
+/**
+ * @constructor
+ * @extends {WebInspector.Panel}
+ * @implements {WebInspector.ContextMenu.Provider}
+ */
+WebInspector.NetworkPanel = function()
+{
+    WebInspector.Panel.call(this, "network");
+    this.registerRequiredCSS("networkPanel.css");
+    this._injectStyles();
+
+    this.createSidebarView();
+    this.splitView.hideMainElement();
+
+    var defaultColumnsVisibility = WebInspector.NetworkLogView._defaultColumnsVisivility;
+    var networkLogColumnsVisibilitySetting = WebInspector.settings.createSetting("networkLogColumnsVisibility", defaultColumnsVisibility);
+    var savedColumnsVisibility = networkLogColumnsVisibilitySetting.get();
+    var columnsVisibility = {};
+    for (var columnId in defaultColumnsVisibility)
+        columnsVisibility[columnId] = savedColumnsVisibility.hasOwnProperty(columnId) ? savedColumnsVisibility[columnId] : defaultColumnsVisibility[columnId];
+    networkLogColumnsVisibilitySetting.set(columnsVisibility);
+
+    this._networkLogView = new WebInspector.NetworkLogView(networkLogColumnsVisibilitySetting);
+    this._networkLogView.show(this.sidebarElement);
+
+    this._viewsContainerElement = this.splitView.mainElement;
+    this._viewsContainerElement.id = "network-views";
+    this._viewsContainerElement.addStyleClass("hidden");
+    if (!this._networkLogView.useLargeRows)
+        this._viewsContainerElement.addStyleClass("small");
+
+    this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.ViewCleared, this._onViewCleared, this);
+    this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RowSizeChanged, this._onRowSizeChanged, this);
+    this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
+    this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
+    this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
+
+    this._closeButtonElement = this._viewsContainerElement.createChild("div", "close-button");
+    this._closeButtonElement.id = "network-close-button";
+    this._closeButtonElement.addEventListener("click", this._toggleGridMode.bind(this), false);
+    this._viewsContainerElement.appendChild(this._closeButtonElement);
+
+    function viewGetter()
+    {
+        return this.visibleView;
+    }
+    WebInspector.GoToLineDialog.install(this, viewGetter.bind(this));
+}
+
+WebInspector.NetworkPanel.prototype = {
+    get statusBarItems()
+    {
+        return this._networkLogView.statusBarItems;
+    },
+
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return this._networkLogView.elementsToRestoreScrollPositionsFor();
+    },
+
+    // FIXME: only used by the layout tests, should not be exposed.
+    _reset: function()
+    {
+        this._networkLogView._reset();
+    },
+
+    handleShortcut: function(event)
+    {
+        if (this._viewingRequestMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
+            this._toggleGridMode();
+            event.handled = true;
+            return;
+        }
+
+        WebInspector.Panel.prototype.handleShortcut.call(this, event);
+    },
+
+    wasShown: function()
+    {
+        WebInspector.Panel.prototype.wasShown.call(this);
+    },
+
+    get requests()
+    {
+        return this._networkLogView.requests;
+    },
+
+    requestById: function(id)
+    {
+        return this._networkLogView.requestById(id);
+    },
+
+    _requestByAnchor: function(anchor)
+    {
+        return anchor.requestId ? this.requestById(anchor.requestId) : this._networkLogView._requestsByURL[anchor.href];
+    },
+
+    canShowAnchorLocation: function(anchor)
+    {
+        return !!this._requestByAnchor(anchor);
+    },
+
+    showAnchorLocation: function(anchor)
+    {
+        var request = this._requestByAnchor(anchor);
+        this.revealAndHighlightRequest(request)
+    },
+
+    revealAndHighlightRequest: function(request)
+    {
+        this._toggleGridMode();
+        if (request)
+            this._networkLogView.revealAndHighlightRequest(request);
+    },
+
+    _onViewCleared: function(event)
+    {
+        this._closeVisibleRequest();
+        this._toggleGridMode();
+        this._viewsContainerElement.removeChildren();
+        this._viewsContainerElement.appendChild(this._closeButtonElement);
+    },
+
+    _onRowSizeChanged: function(event)
+    {
+        this._viewsContainerElement.enableStyleClass("small", !event.data.largeRows);
+    },
+
+    _onSearchCountUpdated: function(event)
+    {
+        WebInspector.searchController.updateSearchMatchesCount(event.data, this);
+    },
+
+    _onSearchIndexUpdated: function(event)
+    {
+        WebInspector.searchController.updateCurrentMatchIndex(event.data, this);
+    },
+
+    _onRequestSelected: function(event)
+    {
+        this._showRequest(event.data);
+    },
+
+    _showRequest: function(request)
+    {
+        if (!request)
+            return;
+
+        this._toggleViewingRequestMode();
+
+        if (this.visibleView) {
+            this.visibleView.detach();
+            delete this.visibleView;
+        }
+
+        var view = new WebInspector.NetworkItemView(request);
+        view.show(this._viewsContainerElement);
+        this.visibleView = view;
+    },
+
+    _closeVisibleRequest: function()
+    {
+        this.element.removeStyleClass("viewing-resource");
+
+        if (this.visibleView) {
+            this.visibleView.detach();
+            delete this.visibleView;
+        }
+    },
+
+    _toggleGridMode: function()
+    {
+        if (this._viewingRequestMode) {
+            this._viewingRequestMode = false;
+            this.element.removeStyleClass("viewing-resource");
+            this.splitView.hideMainElement();
+        }
+
+        this._networkLogView.switchToDetailedView();
+        this._networkLogView.allowPopover = true;
+        this._networkLogView._allowRequestSelection = false;
+    },
+
+    _toggleViewingRequestMode: function()
+    {
+        if (this._viewingRequestMode)
+            return;
+        this._viewingRequestMode = true;
+
+        this.element.addStyleClass("viewing-resource");
+        this.splitView.showMainElement();
+        this._networkLogView.allowPopover = false;
+        this._networkLogView._allowRequestSelection = true;
+        this._networkLogView.switchToBriefView();
+    },
+
+    /**
+     * @param {string} searchQuery
+     */
+    performSearch: function(searchQuery)
+    {
+        this._networkLogView.performSearch(searchQuery);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canFilter: function()
+    {
+        return true;
+    },
+
+    /**
+     * @param {string} query
+     */
+    performFilter: function(query)
+    {
+        this._networkLogView.performFilter(query);
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        this._networkLogView.jumpToPreviousSearchResult();
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        this._networkLogView.jumpToNextSearchResult();
+    },
+
+    searchCanceled: function()
+    {
+        this._networkLogView.searchCanceled();
+    },
+
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    appendApplicableItems: function(event, contextMenu, target)
+    {
+        if (!(target instanceof WebInspector.NetworkRequest))
+            return;
+        if (this.visibleView && this.visibleView.isShowing() && this.visibleView.request() === target)
+            return;
+
+        function reveal()
+        {
+            WebInspector.inspectorView.setCurrentPanel(this);
+            this.revealAndHighlightRequest(/** @type {WebInspector.NetworkRequest} */ (target));
+        }
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Network panel" : "Reveal in Network Panel"), reveal.bind(this));
+    },
+
+    _injectStyles: function()
+    {
+        var style = document.createElement("style");
+        var rules = [];
+
+        var columns = WebInspector.NetworkLogView._defaultColumnsVisivility;
+
+        var hideSelectors = [];
+        var bgSelectors = [];
+        for (var columnId in columns) {
+            hideSelectors.push("#network-container .hide-" + columnId + "-column ." + columnId + "-column");
+            bgSelectors.push(".network-log-grid.data-grid td." + columnId + "-column");
+        }
+        rules.push(hideSelectors.join(", ") + "{border-right: 0 none transparent;}");
+        rules.push(bgSelectors.join(", ") + "{background-color: rgba(0, 0, 0, 0.07);}");
+
+        style.textContent = rules.join("\n");
+        document.head.appendChild(style);
+    },
+
+    __proto__: WebInspector.Panel.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelineGrid.Calculator}
+ */
+WebInspector.NetworkBaseCalculator = function()
+{
+}
+
+WebInspector.NetworkBaseCalculator.prototype = {
+    computePosition: function(time)
+    {
+        return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea;
+    },
+
+    computeBarGraphPercentages: function(item)
+    {
+        return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan()) * 100};
+    },
+
+    computeBarGraphLabels: function(item)
+    {
+        const label = this.formatTime(this._value(item));
+        return {left: label, right: label, tooltip: label};
+    },
+
+    boundarySpan: function()
+    {
+        return this._maximumBoundary - this._minimumBoundary;
+    },
+
+    updateBoundaries: function(item)
+    {
+        this._minimumBoundary = 0;
+
+        var value = this._value(item);
+        if (typeof this._maximumBoundary === "undefined" || value > this._maximumBoundary) {
+            this._maximumBoundary = value;
+            return true;
+        }
+        return false;
+    },
+
+    reset: function()
+    {
+        delete this._minimumBoundary;
+        delete this._maximumBoundary;
+    },
+
+    maximumBoundary: function()
+    {
+        return this._maximumBoundary;
+    },
+
+    minimumBoundary: function()
+    {
+        return this._minimumBoundary;
+    },
+
+    zeroTime: function()
+    {
+        return this._minimumBoundary;
+    },
+
+    _value: function(item)
+    {
+        return 0;
+    },
+
+    formatTime: function(value)
+    {
+        return value.toString();
+    },
+
+    setDisplayWindow: function(clientWidth)
+    {
+        this._workingArea = clientWidth;
+        this.paddingLeft = 0;
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NetworkBaseCalculator}
+ */
+WebInspector.NetworkTimeCalculator = function(startAtZero)
+{
+    WebInspector.NetworkBaseCalculator.call(this);
+    this.startAtZero = startAtZero;
+}
+
+WebInspector.NetworkTimeCalculator.prototype = {
+    computeBarGraphPercentages: function(request)
+    {
+        if (request.startTime !== -1)
+            var start = ((request.startTime - this._minimumBoundary) / this.boundarySpan()) * 100;
+        else
+            var start = 0;
+
+        if (request.responseReceivedTime !== -1)
+            var middle = ((request.responseReceivedTime - this._minimumBoundary) / this.boundarySpan()) * 100;
+        else
+            var middle = (this.startAtZero ? start : 100);
+
+        if (request.endTime !== -1)
+            var end = ((request.endTime - this._minimumBoundary) / this.boundarySpan()) * 100;
+        else
+            var end = (this.startAtZero ? middle : 100);
+
+        if (this.startAtZero) {
+            end -= start;
+            middle -= start;
+            start = 0;
+        }
+
+        return {start: start, middle: middle, end: end};
+    },
+
+    computePercentageFromEventTime: function(eventTime)
+    {
+        // This function computes a percentage in terms of the total loading time
+        // of a specific event. If startAtZero is set, then this is useless, and we
+        // want to return 0.
+        if (eventTime !== -1 && !this.startAtZero)
+            return ((eventTime - this._minimumBoundary) / this.boundarySpan()) * 100;
+
+        return 0;
+    },
+
+    updateBoundariesForEventTime: function(eventTime)
+    {
+        if (eventTime === -1 || this.startAtZero)
+            return false;
+
+        if (typeof this._maximumBoundary === "undefined" || eventTime > this._maximumBoundary) {
+            this._maximumBoundary = eventTime;
+            return true;
+        }
+        return false;
+    },
+
+    computeBarGraphLabels: function(request)
+    {
+        var rightLabel = "";
+        if (request.responseReceivedTime !== -1 && request.endTime !== -1)
+            rightLabel = this.formatTime(request.endTime - request.responseReceivedTime);
+
+        var hasLatency = request.latency > 0;
+        if (hasLatency)
+            var leftLabel = this.formatTime(request.latency);
+        else
+            var leftLabel = rightLabel;
+
+        if (request.timing)
+            return {left: leftLabel, right: rightLabel};
+
+        if (hasLatency && rightLabel) {
+            var total = this.formatTime(request.duration);
+            var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total);
+        } else if (hasLatency)
+            var tooltip = WebInspector.UIString("%s latency", leftLabel);
+        else if (rightLabel)
+            var tooltip = WebInspector.UIString("%s download", rightLabel);
+
+        if (request.cached)
+            tooltip = WebInspector.UIString("%s (from cache)", tooltip);
+        return {left: leftLabel, right: rightLabel, tooltip: tooltip};
+    },
+
+    updateBoundaries: function(request)
+    {
+        var didChange = false;
+
+        var lowerBound;
+        if (this.startAtZero)
+            lowerBound = 0;
+        else
+            lowerBound = this._lowerBound(request);
+
+        if (lowerBound !== -1 && (typeof this._minimumBoundary === "undefined" || lowerBound < this._minimumBoundary)) {
+            this._minimumBoundary = lowerBound;
+            didChange = true;
+        }
+
+        var upperBound = this._upperBound(request);
+        if (upperBound !== -1 && (typeof this._maximumBoundary === "undefined" || upperBound > this._maximumBoundary)) {
+            this._maximumBoundary = upperBound;
+            didChange = true;
+        }
+
+        return didChange;
+    },
+
+    formatTime: function(value)
+    {
+        return Number.secondsToString(value);
+    },
+
+    _lowerBound: function(request)
+    {
+        return 0;
+    },
+
+    _upperBound: function(request)
+    {
+        return 0;
+    },
+
+    __proto__: WebInspector.NetworkBaseCalculator.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NetworkTimeCalculator}
+ */
+WebInspector.NetworkTransferTimeCalculator = function()
+{
+    WebInspector.NetworkTimeCalculator.call(this, false);
+}
+
+WebInspector.NetworkTransferTimeCalculator.prototype = {
+    formatTime: function(value)
+    {
+        return Number.secondsToString(value);
+    },
+
+    _lowerBound: function(request)
+    {
+        return request.startTime;
+    },
+
+    _upperBound: function(request)
+    {
+        return request.endTime;
+    },
+
+    __proto__: WebInspector.NetworkTimeCalculator.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NetworkTimeCalculator}
+ */
+WebInspector.NetworkTransferDurationCalculator = function()
+{
+    WebInspector.NetworkTimeCalculator.call(this, true);
+}
+
+WebInspector.NetworkTransferDurationCalculator.prototype = {
+    formatTime: function(value)
+    {
+        return Number.secondsToString(value);
+    },
+
+    _upperBound: function(request)
+    {
+        return request.duration;
+    },
+
+    __proto__: WebInspector.NetworkTimeCalculator.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ * @param {!WebInspector.NetworkLogView} parentView
+ * @param {!WebInspector.NetworkRequest} request
+ */
+WebInspector.NetworkDataGridNode = function(parentView, request)
+{
+    WebInspector.DataGridNode.call(this, {});
+    this._parentView = parentView;
+    this._request = request;
+    this._linkifier = new WebInspector.Linkifier();
+}
+
+WebInspector.NetworkDataGridNode.prototype = {
+    /** override */
+    createCells: function()
+    {
+        // Out of sight, out of mind: create nodes offscreen to save on render tree update times when running updateOffscreenRows()
+        this._element.addStyleClass("offscreen");
+        this._nameCell = this._createDivInTD("name");
+        this._methodCell = this._createDivInTD("method");
+        this._statusCell = this._createDivInTD("status");
+        this._domainCell = this._createDivInTD("domain");
+        this._typeCell = this._createDivInTD("type");
+        this._initiatorCell = this._createDivInTD("initiator");
+        this._cookiesCell = this._createDivInTD("cookies");
+        this._setCookiesCell = this._createDivInTD("setCookies");
+        this._sizeCell = this._createDivInTD("size");
+        this._timeCell = this._createDivInTD("time");
+        this._timelineCell = this._createDivInTD("timeline");
+        this._createTimelineBar(this._timelineCell);
+        this._nameCell.addEventListener("click", this._onClick.bind(this), false);
+        this._nameCell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
+    },
+
+    wasDetached: function()
+    {
+        this._linkifier.reset();
+    },
+
+    isFilteredOut: function()
+    {
+        if (this._parentView._filteredOutRequests.get(this._request))
+            return true;
+        return !this._parentView._typeFilter(this._request);
+    },
+
+    _onClick: function()
+    {
+        if (!this._parentView._allowRequestSelection)
+            this.select();
+    },
+
+    select: function()
+    {
+        this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
+        WebInspector.DataGridNode.prototype.select.apply(this, arguments);
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.NetworkRequestSelected,
+            url: this._request.url
+        });
+    },
+
+    _highlightMatchedSubstring: function(regexp)
+    {
+        var domChanges = [];
+        var matchInfo = this._element.textContent.match(regexp);
+        if (matchInfo)
+            WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
+        return domChanges;
+    },
+
+    _openInNewTab: function()
+    {
+        InspectorFrontendHost.openInNewTab(this._request.url);
+    },
+
+    get selectable()
+    {
+        return this._parentView._allowRequestSelection && !this.isFilteredOut();
+    },
+
+    _createDivInTD: function(columnIdentifier)
+    {
+        var td = this.createTD(columnIdentifier);
+        var div = td.createChild("div");
+        this._element.appendChild(td);
+        return div;
+    },
+
+    /**
+     * @param {!Element} cell
+     */
+    _createTimelineBar: function(cell)
+    {
+        cell.className = "network-graph-side";
+
+        this._barAreaElement = document.createElement("div");
+        //    this._barAreaElement.className = "network-graph-bar-area hidden";
+        this._barAreaElement.className = "network-graph-bar-area";
+        this._barAreaElement.request = this._request;
+        cell.appendChild(this._barAreaElement);
+
+        this._barLeftElement = document.createElement("div");
+        this._barLeftElement.className = "network-graph-bar waiting";
+        this._barAreaElement.appendChild(this._barLeftElement);
+
+        this._barRightElement = document.createElement("div");
+        this._barRightElement.className = "network-graph-bar";
+        this._barAreaElement.appendChild(this._barRightElement);
+
+
+        this._labelLeftElement = document.createElement("div");
+        this._labelLeftElement.className = "network-graph-label waiting";
+        this._barAreaElement.appendChild(this._labelLeftElement);
+
+        this._labelRightElement = document.createElement("div");
+        this._labelRightElement.className = "network-graph-label";
+        this._barAreaElement.appendChild(this._labelRightElement);
+
+        cell.addEventListener("mouseover", this._refreshLabelPositions.bind(this), false);
+    },
+
+    refreshRequest: function()
+    {
+        this._refreshNameCell();
+        this._refreshMethodCell();
+        this._refreshStatusCell();
+        this._refreshDomainCell();
+        this._refreshTypeCell();
+        this._refreshInitiatorCell();
+        this._refreshCookiesCell();
+        this._refreshSetCookiesCell();
+        this._refreshSizeCell();
+        this._refreshTimeCell();
+
+        if (this._request.cached)
+            this._timelineCell.addStyleClass("resource-cached");
+
+        this._element.addStyleClass("network-item");
+        this._element.enableStyleClass("network-error-row", this._request.failed || (this._request.statusCode >= 400));
+        this._updateElementStyleClasses(this._element);
+    },
+
+    /**
+     * @param {!Element} element
+     */
+    _updateElementStyleClasses: function(element)
+    {
+        var typeClassName = "network-type-" + this._request.type.name();
+        if (!element.hasStyleClass(typeClassName)) {
+            element.removeMatchingStyleClasses("network-type-\\w+");
+            element.addStyleClass(typeClassName);
+        }
+    },
+
+    _refreshNameCell: function()
+    {
+        this._nameCell.removeChildren();
+
+        if (this._request.type === WebInspector.resourceTypes.Image) {
+            var previewImage = document.createElement("img");
+            previewImage.className = "image-network-icon-preview";
+            this._request.populateImageSource(previewImage);
+
+            var iconElement = document.createElement("div");
+            iconElement.className = "icon";
+            iconElement.appendChild(previewImage);
+        } else {
+            var iconElement = document.createElement("img");
+            iconElement.className = "icon";
+        }
+        this._nameCell.appendChild(iconElement);
+        this._nameCell.appendChild(document.createTextNode(this._request.name()));
+        this._appendSubtitle(this._nameCell, this._request.path());
+        this._nameCell.title = this._request.url;
+    },
+
+    _refreshMethodCell: function()
+    {
+        this._methodCell.setTextAndTitle(this._request.requestMethod);
+    },
+
+    _refreshStatusCell: function()
+    {
+        this._statusCell.removeChildren();
+
+        if (this._request.failed) {
+            var failText = this._request.canceled ? WebInspector.UIString("(canceled)") : WebInspector.UIString("(failed)");
+            if (this._request.localizedFailDescription) {
+                this._statusCell.appendChild(document.createTextNode(failText));
+                this._appendSubtitle(this._statusCell, this._request.localizedFailDescription);
+                this._statusCell.title = failText + " " + this._request.localizedFailDescription;
+            } else
+                this._statusCell.setTextAndTitle(failText);
+            this._statusCell.addStyleClass("network-dim-cell");
+            return;
+        }
+
+        this._statusCell.removeStyleClass("network-dim-cell");
+
+        if (this._request.statusCode) {
+            this._statusCell.appendChild(document.createTextNode("" + this._request.statusCode));
+            this._appendSubtitle(this._statusCell, this._request.statusText);
+            this._statusCell.title = this._request.statusCode + " " + this._request.statusText;
+            if (this._request.cached)
+                this._statusCell.addStyleClass("network-dim-cell");
+        } else {
+            if (!this._request.isHttpFamily() && this._request.finished)
+                this._statusCell.setTextAndTitle(WebInspector.UIString("Success"));
+            else if (this._request.isPingRequest())
+                this._statusCell.setTextAndTitle(WebInspector.UIString("(ping)"));
+            else
+                this._statusCell.setTextAndTitle(WebInspector.UIString("(pending)"));
+            this._statusCell.addStyleClass("network-dim-cell");
+        }
+    },
+
+    _refreshDomainCell: function()
+    {
+        this._domainCell.removeChildren();
+        this._domainCell.appendChild(document.createTextNode(this._request.domain));
+        this._domainCell.title = this._request.parsedURL.host;
+    },
+
+    _refreshTypeCell: function()
+    {
+        if (this._request.mimeType) {
+            this._typeCell.removeStyleClass("network-dim-cell");
+            this._typeCell.setTextAndTitle(this._request.mimeType);
+        } else if (this._request.isPingRequest()) {
+            this._typeCell.removeStyleClass("network-dim-cell");
+            this._typeCell.setTextAndTitle(this._request.requestContentType() || "");
+        } else {
+            this._typeCell.addStyleClass("network-dim-cell");
+            this._typeCell.setTextAndTitle(WebInspector.UIString("Pending"));
+        }
+    },
+
+    _refreshInitiatorCell: function()
+    {
+        this._initiatorCell.removeChildren();
+        this._initiatorCell.removeStyleClass("network-dim-cell");
+        this._initiatorCell.removeStyleClass("network-script-initiated");
+        delete this._initiatorCell.request;
+
+        var request = this._request;
+        var initiator = request.initiatorInfo();
+
+        switch (initiator.type) {
+        case WebInspector.NetworkRequest.InitiatorType.Parser:
+            this._initiatorCell.title = initiator.url + ":" + initiator.lineNumber;
+            this._initiatorCell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1));
+            this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Parser"));
+            break;
+
+        case WebInspector.NetworkRequest.InitiatorType.Redirect:
+            this._initiatorCell.title = initiator.url;
+            this._initiatorCell.appendChild(WebInspector.linkifyRequestAsNode(request.redirectSource));
+            this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Redirect"));
+            break;
+
+        case WebInspector.NetworkRequest.InitiatorType.Script:
+            var urlElement = this._linkifier.linkifyLocation(initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
+            urlElement.title = "";
+            this._initiatorCell.appendChild(urlElement);
+            this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Script"));
+            this._initiatorCell.addStyleClass("network-script-initiated");
+            this._initiatorCell.request = request;
+            break;
+
+        default:
+            this._initiatorCell.title = "";
+            this._initiatorCell.addStyleClass("network-dim-cell");
+            this._initiatorCell.setTextAndTitle(WebInspector.UIString("Other"));
+        }
+    },
+
+    _refreshCookiesCell: function()
+    {
+        var requestCookies = this._request.requestCookies;
+        this._cookiesCell.setTextAndTitle(requestCookies ? "" + requestCookies.length : "");
+    },
+
+    _refreshSetCookiesCell: function()
+    {
+        var responseCookies = this._request.responseCookies;
+        this._setCookiesCell.setTextAndTitle(responseCookies ? "" + responseCookies.length : "");
+    },
+
+    _refreshSizeCell: function()
+    {
+        if (this._request.cached) {
+            this._sizeCell.setTextAndTitle(WebInspector.UIString("(from cache)"));
+            this._sizeCell.addStyleClass("network-dim-cell");
+        } else {
+            var resourceSize = typeof this._request.resourceSize === "number" ? Number.bytesToString(this._request.resourceSize) : "?";
+            var transferSize = typeof this._request.transferSize === "number" ? Number.bytesToString(this._request.transferSize) : "?";
+            this._sizeCell.setTextAndTitle(transferSize);
+            this._sizeCell.removeStyleClass("network-dim-cell");
+            this._appendSubtitle(this._sizeCell, resourceSize);
+        }
+    },
+
+    _refreshTimeCell: function()
+    {
+        if (this._request.duration > 0) {
+            this._timeCell.removeStyleClass("network-dim-cell");
+            this._timeCell.setTextAndTitle(Number.secondsToString(this._request.duration));
+            this._appendSubtitle(this._timeCell, Number.secondsToString(this._request.latency));
+        } else {
+            this._timeCell.addStyleClass("network-dim-cell");
+            this._timeCell.setTextAndTitle(WebInspector.UIString("Pending"));
+        }
+    },
+
+    _appendSubtitle: function(cellElement, subtitleText)
+    {
+        var subtitleElement = document.createElement("div");
+        subtitleElement.className = "network-cell-subtitle";
+        subtitleElement.textContent = subtitleText;
+        cellElement.appendChild(subtitleElement);
+    },
+
+    refreshGraph: function(calculator)
+    {
+        var percentages = calculator.computeBarGraphPercentages(this._request);
+        this._percentages = percentages;
+
+        this._barAreaElement.removeStyleClass("hidden");
+        this._updateElementStyleClasses(this._timelineCell);
+
+        this._barLeftElement.style.setProperty("left", percentages.start + "%");
+        this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
+
+        this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%");
+        this._barRightElement.style.setProperty("left", percentages.middle + "%");
+
+        var labels = calculator.computeBarGraphLabels(this._request);
+        this._labelLeftElement.textContent = labels.left;
+        this._labelRightElement.textContent = labels.right;
+
+        var tooltip = (labels.tooltip || "");
+        this._barLeftElement.title = tooltip;
+        this._labelLeftElement.title = tooltip;
+        this._labelRightElement.title = tooltip;
+        this._barRightElement.title = tooltip;
+    },
+
+    _refreshLabelPositions: function()
+    {
+        if (!this._percentages)
+            return;
+        this._labelLeftElement.style.removeProperty("left");
+        this._labelLeftElement.style.removeProperty("right");
+        this._labelLeftElement.removeStyleClass("before");
+        this._labelLeftElement.removeStyleClass("hidden");
+
+        this._labelRightElement.style.removeProperty("left");
+        this._labelRightElement.style.removeProperty("right");
+        this._labelRightElement.removeStyleClass("after");
+        this._labelRightElement.removeStyleClass("hidden");
+
+        const labelPadding = 10;
+        const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
+        const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
+
+        if (this._barLeftElement) {
+            var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
+            var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
+        } else {
+            var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
+            var rightBarWidth = barRightElementOffsetWidth - labelPadding;
+        }
+
+        const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
+        const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
+
+        const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
+        const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
+        const graphElementOffsetWidth = this._timelineCell.offsetWidth;
+
+        if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
+            var leftHidden = true;
+
+        if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
+            var rightHidden = true;
+
+        if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
+            // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
+            if (labelBefore && !labelAfter)
+                leftHidden = true;
+            else if (labelAfter && !labelBefore)
+                rightHidden = true;
+        }
+
+        if (labelBefore) {
+            if (leftHidden)
+                this._labelLeftElement.addStyleClass("hidden");
+            this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
+            this._labelLeftElement.addStyleClass("before");
+        } else {
+            this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
+            this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
+        }
+
+        if (labelAfter) {
+            if (rightHidden)
+                this._labelRightElement.addStyleClass("hidden");
+            this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
+            this._labelRightElement.addStyleClass("after");
+        } else {
+            this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
+            this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
+        }
+    },
+
+    __proto__: WebInspector.DataGridNode.prototype
+}
+
+/**
+ * @param {WebInspector.NetworkRequest} request
+ * @return {boolean}
+ */
+WebInspector.NetworkLogView._trivialTypeFilter = function(request)
+{
+    return true;
+}
+
+/**
+ * @param {!Object.<string, boolean>} allowedTypes
+ * @param {WebInspector.NetworkRequest} request
+ * @return {boolean}
+ */
+WebInspector.NetworkLogView._typeFilter = function(allowedTypes, request)
+{
+    return request.type.name() in allowedTypes;
+}
+
+
+WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
+{
+    var aFileName = a._request.name();
+    var bFileName = b._request.name();
+    if (aFileName > bFileName)
+        return 1;
+    if (bFileName > aFileName)
+        return -1;
+    return 0;
+}
+
+WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
+{
+    if (b._request.cached && !a._request.cached)
+        return 1;
+    if (a._request.cached && !b._request.cached)
+        return -1;
+
+    if (a._request.transferSize === b._request.transferSize)
+        return 0;
+
+    return a._request.transferSize - b._request.transferSize;
+}
+
+WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
+{
+    var aInitiator = a._request.initiatorInfo();
+    var bInitiator = b._request.initiatorInfo();
+
+    if (aInitiator.type < bInitiator.type)
+        return -1;
+    if (aInitiator.type > bInitiator.type)
+        return 1;
+
+    if (aInitiator.source < bInitiator.source)
+        return -1;
+    if (aInitiator.source > bInitiator.source)
+        return 1;
+
+    if (aInitiator.lineNumber < bInitiator.lineNumber)
+        return -1;
+    if (aInitiator.lineNumber > bInitiator.lineNumber)
+        return 1;
+
+    if (aInitiator.columnNumber < bInitiator.columnNumber)
+        return -1;
+    if (aInitiator.columnNumber > bInitiator.columnNumber)
+        return 1;
+
+    return 0;
+}
+
+WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
+{
+    var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
+    var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
+    return aScore - bScore;
+}
+
+WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
+{
+    var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
+    var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
+    return aScore - bScore;
+}
+
+WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
+{
+    var aValue = a._request[propertyName];
+    var bValue = b._request[propertyName];
+    if (aValue > bValue)
+        return revert ? -1 : 1;
+    if (bValue > aValue)
+        return revert ? 1 : -1;
+    return 0;
+}
diff --git a/Source/devtools/front_end/NetworkPanelDescriptor.js b/Source/devtools/front_end/NetworkPanelDescriptor.js
new file mode 100644
index 0000000..a567583
--- /dev/null
+++ b/Source/devtools/front_end/NetworkPanelDescriptor.js
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.PanelDescriptor}
+ * @implements {WebInspector.ContextMenu.Provider}
+ */
+WebInspector.NetworkPanelDescriptor = function()
+{
+    WebInspector.PanelDescriptor.call(this, "network", WebInspector.UIString("Network"), "NetworkPanel", "NetworkPanel.js");
+    WebInspector.ContextMenu.registerProvider(this);
+}
+
+WebInspector.NetworkPanelDescriptor.prototype = {
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    appendApplicableItems: function(event, contextMenu, target)
+    {
+        if (!(target instanceof WebInspector.NetworkRequest))
+            return;
+        this.panel().appendApplicableItems(event, contextMenu, target);
+    },
+
+    __proto__: WebInspector.PanelDescriptor.prototype
+}
diff --git a/Source/devtools/front_end/NetworkRequest.js b/Source/devtools/front_end/NetworkRequest.js
new file mode 100644
index 0000000..8af1318
--- /dev/null
+++ b/Source/devtools/front_end/NetworkRequest.js
@@ -0,0 +1,966 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @implements {WebInspector.ContentProvider}
+ * @param {NetworkAgent.RequestId} requestId
+ * @param {string} url
+ * @param {string} documentURL
+ * @param {NetworkAgent.FrameId} frameId
+ * @param {NetworkAgent.LoaderId} loaderId
+ */
+WebInspector.NetworkRequest = function(requestId, url, documentURL, frameId, loaderId)
+{
+    this._requestId = requestId;
+    this.url = url;
+    this._documentURL = documentURL;
+    this._frameId = frameId;
+    this._loaderId = loaderId;
+    this._startTime = -1;
+    this._endTime = -1;
+
+    this.statusCode = 0;
+    this.statusText = "";
+    this.requestMethod = "";
+    this.requestTime = 0;
+    this.receiveHeadersEnd = 0;
+
+    this._type = WebInspector.resourceTypes.Other;
+    this._contentEncoded = false;
+    this._pendingContentCallbacks = [];
+    this._frames = [];
+}
+
+WebInspector.NetworkRequest.Events = {
+    FinishedLoading: "FinishedLoading",
+    TimingChanged: "TimingChanged",
+    RequestHeadersChanged: "RequestHeadersChanged",
+    ResponseHeadersChanged: "ResponseHeadersChanged",
+}
+
+/** @enum {string} */
+WebInspector.NetworkRequest.InitiatorType = {
+    Other: "other",
+    Parser: "parser",
+    Redirect: "redirect",
+    Script: "script"
+}
+
+/** @typedef {{name: string, value: string}} */
+WebInspector.NetworkRequest.NameValue;
+
+WebInspector.NetworkRequest.prototype = {
+    /**
+     * @return {NetworkAgent.RequestId}
+     */
+    get requestId()
+    {
+        return this._requestId;
+    },
+
+    set requestId(requestId)
+    {
+        this._requestId = requestId;
+    },
+
+    /**
+     * @return {string}
+     */
+    get url()
+    {
+        return this._url;
+    },
+
+    set url(x)
+    {
+        if (this._url === x)
+            return;
+
+        this._url = x;
+        this._parsedURL = new WebInspector.ParsedURL(x);
+        delete this._parsedQueryParameters;
+        delete this._name;
+        delete this._path;
+    },
+
+    /**
+     * @return {string}
+     */
+    get documentURL()
+    {
+        return this._documentURL;
+    },
+
+    get parsedURL()
+    {
+        return this._parsedURL;
+    },
+
+    /**
+     * @return {NetworkAgent.FrameId}
+     */
+    get frameId()
+    {
+        return this._frameId;
+    },
+
+    /**
+     * @return {NetworkAgent.LoaderId}
+     */
+    get loaderId()
+    {
+        return this._loaderId;
+    },
+
+    /**
+     * @return {number}
+     */
+    get startTime()
+    {
+        return this._startTime || -1;
+    },
+
+    set startTime(x)
+    {
+        this._startTime = x;
+    },
+
+    /**
+     * @return {number}
+     */
+    get responseReceivedTime()
+    {
+        return this._responseReceivedTime || -1;
+    },
+
+    set responseReceivedTime(x)
+    {
+        this._responseReceivedTime = x;
+    },
+
+    /**
+     * @return {number}
+     */
+    get endTime()
+    {
+        return this._endTime || -1;
+    },
+
+    set endTime(x)
+    {
+        if (this.timing && this.timing.requestTime) {
+            // Check against accurate responseReceivedTime.
+            this._endTime = Math.max(x, this.responseReceivedTime);
+        } else {
+            // Prefer endTime since it might be from the network stack.
+            this._endTime = x;
+            if (this._responseReceivedTime > x)
+                this._responseReceivedTime = x;
+        }
+    },
+
+    /**
+     * @return {number}
+     */
+    get duration()
+    {
+        if (this._endTime === -1 || this._startTime === -1)
+            return -1;
+        return this._endTime - this._startTime;
+    },
+
+    /**
+     * @return {number}
+     */
+    get latency()
+    {
+        if (this._responseReceivedTime === -1 || this._startTime === -1)
+            return -1;
+        return this._responseReceivedTime - this._startTime;
+    },
+
+    /**
+     * @return {number}
+     */
+    get receiveDuration()
+    {
+        if (this._endTime === -1 || this._responseReceivedTime === -1)
+            return -1;
+        return this._endTime - this._responseReceivedTime;
+    },
+
+    /**
+     * @return {number}
+     */
+    get resourceSize()
+    {
+        return this._resourceSize || 0;
+    },
+
+    set resourceSize(x)
+    {
+        this._resourceSize = x;
+    },
+
+    /**
+     * @return {number}
+     */
+    get transferSize()
+    {
+        if (this.cached)
+            return 0;
+        if (this.statusCode === 304) // Not modified
+            return this.responseHeadersSize;
+        if (this._transferSize !== undefined)
+            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.
+        var bodySize = Number(this.responseHeaderValue("Content-Length") || this.resourceSize);
+        return this.responseHeadersSize + bodySize;
+    },
+
+    /**
+     * @param {number} x
+     */
+    increaseTransferSize: function(x)
+    {
+        this._transferSize = (this._transferSize || 0) + x;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    get finished()
+    {
+        return this._finished;
+    },
+
+    set finished(x)
+    {
+        if (this._finished === x)
+            return;
+
+        this._finished = x;
+
+        if (x) {
+            this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.FinishedLoading, this);
+            if (this._pendingContentCallbacks.length)
+                this._innerRequestContent();
+        }
+    },
+
+    /**
+     * @return {boolean}
+     */
+    get failed()
+    {
+        return this._failed;
+    },
+
+    set failed(x)
+    {
+        this._failed = x;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    get canceled()
+    {
+        return this._canceled;
+    },
+
+    set canceled(x)
+    {
+        this._canceled = x;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    get cached()
+    {
+        return this._cached;
+    },
+
+    set cached(x)
+    {
+        this._cached = x;
+        if (x)
+            delete this._timing;
+    },
+
+    /**
+     * @return {NetworkAgent.ResourceTiming|undefined}
+     */
+    get timing()
+    {
+        return this._timing;
+    },
+
+    set timing(x)
+    {
+        if (x && !this._cached) {
+            // Take startTime and responseReceivedTime from timing data for better accuracy.
+            // Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis.
+            this._startTime = x.requestTime;
+            this._responseReceivedTime = x.requestTime + x.receiveHeadersEnd / 1000.0;
+
+            this._timing = x;
+            this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.TimingChanged, this);
+        }
+    },
+
+    /**
+     * @return {string}
+     */
+    get mimeType()
+    {
+        return this._mimeType;
+    },
+
+    set mimeType(x)
+    {
+        this._mimeType = x;
+    },
+
+    /**
+     * @return {string}
+     */
+    get displayName()
+    {
+        return this._parsedURL.displayName;
+    },
+
+    name: function()
+    {
+        if (this._name)
+            return this._name;
+        this._parseNameAndPathFromURL();
+        return this._name;
+    },
+
+    path: function()
+    {
+        if (this._path)
+            return this._path;
+        this._parseNameAndPathFromURL();
+        return this._path;
+    },
+
+    _parseNameAndPathFromURL: function()
+    {
+        if (this._parsedURL.isDataURL()) {
+            this._name = this._parsedURL.dataURLDisplayName();
+            this._path = "";
+        } else if (this._parsedURL.isAboutBlank()) {
+            this._name = this._parsedURL.url;
+            this._path = "";
+        } else {
+            this._path = this._parsedURL.host + this._parsedURL.folderPathComponents;
+            this._path = this._path.trimURL(WebInspector.inspectedPageDomain ? WebInspector.inspectedPageDomain : "");
+            if (this._parsedURL.lastPathComponent || this._parsedURL.queryParams)
+                this._name = this._parsedURL.lastPathComponent + (this._parsedURL.queryParams ? "?" + this._parsedURL.queryParams : "");
+            else if (this._parsedURL.folderPathComponents) {
+                this._name = this._parsedURL.folderPathComponents.substring(this._parsedURL.folderPathComponents.lastIndexOf("/") + 1) + "/";
+                this._path = this._path.substring(0, this._path.lastIndexOf("/"));
+            } else {
+                this._name = this._parsedURL.host;
+                this._path = "";
+            }
+        }
+    },
+
+    /**
+     * @return {string}
+     */
+    get folder()
+    {
+        var path = this._parsedURL.path;
+        var indexOfQuery = path.indexOf("?");
+        if (indexOfQuery !== -1)
+            path = path.substring(0, indexOfQuery);
+        var lastSlashIndex = path.lastIndexOf("/");
+        return lastSlashIndex !== -1 ? path.substring(0, lastSlashIndex) : "";
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    get type()
+    {
+        return this._type;
+    },
+
+    set type(x)
+    {
+        this._type = x;
+    },
+
+    /**
+     * @return {string}
+     */
+    get domain()
+    {
+        return this._parsedURL.host;
+    },
+
+    /**
+     * @return {?WebInspector.NetworkRequest}
+     */
+    get redirectSource()
+    {
+        if (this.redirects && this.redirects.length > 0)
+            return this.redirects[this.redirects.length - 1];
+        return this._redirectSource;
+    },
+
+    set redirectSource(x)
+    {
+        this._redirectSource = x;
+        delete this._initiatorInfo;
+    },
+
+    /**
+     * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
+     */
+    get requestHeaders()
+    {
+        return this._requestHeaders || [];
+    },
+
+    set requestHeaders(x)
+    {
+        this._requestHeaders = x;
+        delete this._sortedRequestHeaders;
+        delete this._requestCookies;
+
+        this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.RequestHeadersChanged);
+    },
+
+    /**
+     * @return {string}
+     */
+    get requestHeadersText()
+    {
+        if (typeof this._requestHeadersText === "undefined") {
+            this._requestHeadersText = this.requestMethod + " " + this.url + " HTTP/1.1\r\n";
+            for (var i = 0; i < this.requestHeaders.length; ++i)
+                this._requestHeadersText += this.requestHeaders[i].name + ": " + this.requestHeaders[i].value + "\r\n";
+        }
+        return this._requestHeadersText;
+    },
+
+    set requestHeadersText(x)
+    {
+        this._requestHeadersText = x;
+
+        this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.RequestHeadersChanged);
+    },
+
+    /**
+     * @return {number}
+     */
+    get requestHeadersSize()
+    {
+        return this.requestHeadersText.length;
+    },
+
+    /**
+     * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
+     */
+    get sortedRequestHeaders()
+    {
+        if (this._sortedRequestHeaders !== undefined)
+            return this._sortedRequestHeaders;
+
+        this._sortedRequestHeaders = [];
+        this._sortedRequestHeaders = this.requestHeaders.slice();
+        this._sortedRequestHeaders.sort(function(a,b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()) });
+        return this._sortedRequestHeaders;
+    },
+
+    /**
+     * @param {string} headerName
+     * @return {string|undefined}
+     */
+    requestHeaderValue: function(headerName)
+    {
+        return this._headerValue(this.requestHeaders, headerName);
+    },
+
+    /**
+     * @return {Array.<WebInspector.Cookie>}
+     */
+    get requestCookies()
+    {
+        if (!this._requestCookies)
+            this._requestCookies = WebInspector.CookieParser.parseCookie(this.requestHeaderValue("Cookie"));
+        return this._requestCookies;
+    },
+
+    /**
+     * @return {string|undefined}
+     */
+    get requestFormData()
+    {
+        return this._requestFormData;
+    },
+
+    set requestFormData(x)
+    {
+        this._requestFormData = x;
+        delete this._parsedFormParameters;
+    },
+
+    /**
+     * @return {string|undefined}
+     */
+    get requestHttpVersion()
+    {
+        var firstLine = this.requestHeadersText.split(/\r\n/)[0];
+        var match = firstLine.match(/(HTTP\/\d+\.\d+)$/);
+        return match ? match[1] : undefined;
+    },
+
+    /**
+     * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
+     */
+    get responseHeaders()
+    {
+        return this._responseHeaders || [];
+    },
+
+    set responseHeaders(x)
+    {
+        this._responseHeaders = x;
+        delete this._sortedResponseHeaders;
+        delete this._responseCookies;
+
+        this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.ResponseHeadersChanged);
+    },
+
+    /**
+     * @return {string}
+     */
+    get responseHeadersText()
+    {
+        if (typeof this._responseHeadersText === "undefined") {
+            this._responseHeadersText = "HTTP/1.1 " + this.statusCode + " " + this.statusText + "\r\n";
+            for (var i = 0; i < this.responseHeaders.length; ++i)
+                this._responseHeadersText += this.responseHeaders[i].name + ": " + this.responseHeaders[i].value + "\r\n";
+        }
+        return this._responseHeadersText;
+    },
+
+    set responseHeadersText(x)
+    {
+        this._responseHeadersText = x;
+
+        this.dispatchEventToListeners(WebInspector.NetworkRequest.Events.ResponseHeadersChanged);
+    },
+
+    /**
+     * @return {number}
+     */
+    get responseHeadersSize()
+    {
+        return this.responseHeadersText.length;
+    },
+
+    /**
+     * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
+     */
+    get sortedResponseHeaders()
+    {
+        if (this._sortedResponseHeaders !== undefined)
+            return this._sortedResponseHeaders;
+
+        this._sortedResponseHeaders = [];
+        this._sortedResponseHeaders = this.responseHeaders.slice();
+        this._sortedResponseHeaders.sort(function(a, b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()); });
+        return this._sortedResponseHeaders;
+    },
+
+    /**
+     * @param {string} headerName
+     * @return {string|undefined}
+     */
+    responseHeaderValue: function(headerName)
+    {
+        return this._headerValue(this.responseHeaders, headerName);
+    },
+
+    /**
+     * @return {Array.<WebInspector.Cookie>}
+     */
+    get responseCookies()
+    {
+        if (!this._responseCookies)
+            this._responseCookies = WebInspector.CookieParser.parseSetCookie(this.responseHeaderValue("Set-Cookie"));
+        return this._responseCookies;
+    },
+
+    /**
+     * @return {?string}
+     */
+    queryString: function()
+    {
+        if (this._queryString)
+            return this._queryString;
+        var queryString = this.url.split("?", 2)[1];
+        if (!queryString)
+            return null;
+        this._queryString = queryString.split("#", 2)[0];
+        return this._queryString;
+    },
+
+    /**
+     * @return {?Array.<!WebInspector.NetworkRequest.NameValue>}
+     */
+    get queryParameters()
+    {
+        if (this._parsedQueryParameters)
+            return this._parsedQueryParameters;
+        var queryString = this.queryString();
+        if (!queryString)
+            return null;
+        this._parsedQueryParameters = this._parseParameters(queryString);
+        return this._parsedQueryParameters;
+    },
+
+    /**
+     * @return {?Array.<!WebInspector.NetworkRequest.NameValue>}
+     */
+    get formParameters()
+    {
+        if (this._parsedFormParameters)
+            return this._parsedFormParameters;
+        if (!this.requestFormData)
+            return null;
+        var requestContentType = this.requestContentType();
+        if (!requestContentType || !requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i))
+            return null;
+        this._parsedFormParameters = this._parseParameters(this.requestFormData);
+        return this._parsedFormParameters;
+    },
+
+    /**
+     * @return {string|undefined}
+     */
+    get responseHttpVersion()
+    {
+        var match = this.responseHeadersText.match(/^(HTTP\/\d+\.\d+)/);
+        return match ? match[1] : undefined;
+    },
+
+    /**
+     * @param {string} queryString
+     * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
+     */
+    _parseParameters: function(queryString)
+    {
+        function parseNameValue(pair)
+        {
+            var splitPair = pair.split("=", 2);
+            return {name: splitPair[0], value: splitPair[1] || ""};
+        }
+        return queryString.split("&").map(parseNameValue);
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.NetworkRequest.NameValue>} headers
+     * @param {string} headerName
+     * @return {string|undefined}
+     */
+    _headerValue: function(headers, headerName)
+    {
+        headerName = headerName.toLowerCase();
+
+        var values = [];
+        for (var i = 0; i < headers.length; ++i) {
+            if (headers[i].name.toLowerCase() === headerName)
+                values.push(headers[i].value);
+        }
+        if (!values.length)
+            return undefined;
+        // Set-Cookie values should be separated by '\n', not comma, otherwise cookies could not be parsed.
+        if (headerName === "set-cookie")
+            return values.join("\n");
+        return values.join(", ");
+    },
+
+    /**
+     * @return {?string|undefined}
+     */
+    get content()
+    {
+        return this._content;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    get contentEncoded()
+    {
+        return this._contentEncoded;
+    },
+
+    /**
+     * @return {string}
+     */
+    contentURL: function()
+    {
+        return this._url;
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function()
+    {
+        return this._type;
+    },
+
+    /**
+     * @param {function(?string, boolean, string)} callback
+     */
+    requestContent: function(callback)
+    {
+        // We do not support content retrieval for WebSockets at the moment.
+        // Since WebSockets are potentially long-living, fail requests immediately
+        // to prevent caller blocking until resource is marked as finished.
+        if (this.type === WebInspector.resourceTypes.WebSocket) {
+            callback(null, false, this._mimeType);
+            return;
+        }
+        if (typeof this._content !== "undefined") {
+            callback(this.content || null, this._contentEncoded, this.type.canonicalMimeType() || this._mimeType);
+            return;
+        }
+        this._pendingContentCallbacks.push(callback);
+        if (this.finished)
+            this._innerRequestContent();
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback)
+    {
+        callback([]);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isHttpFamily: function()
+    {
+        return !!this.url.match(/^https?:/i);
+    },
+
+    /**
+     * @return {string|undefined}
+     */
+    requestContentType: function()
+    {
+        return this.requestHeaderValue("Content-Type");
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isPingRequest: function()
+    {
+        return "text/ping" === this.requestContentType();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    hasErrorStatusCode: function()
+    {
+        return this.statusCode >= 400;
+    },
+
+    /**
+     * @param {Element} image
+     */
+    populateImageSource: function(image)
+    {
+        /**
+         * @this {WebInspector.NetworkRequest}
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function onResourceContent(content, contentEncoded, mimeType)
+        {
+            var imageSrc = this.asDataURL();
+            if (imageSrc === null)
+                imageSrc = this.url;
+            image.src = imageSrc;
+        }
+
+        this.requestContent(onResourceContent.bind(this));
+    },
+
+    /**
+     * @return {?string}
+     */
+    asDataURL: function()
+    {
+        return WebInspector.contentAsDataURL(this._content, this.mimeType, this._contentEncoded);
+    },
+
+    _innerRequestContent: function()
+    {
+        if (this._contentRequested)
+            return;
+        this._contentRequested = true;
+
+        /**
+         * @param {?Protocol.Error} error
+         * @param {string} content
+         * @param {boolean} contentEncoded
+         */
+        function onResourceContent(error, content, contentEncoded)
+        {
+            this._content = error ? null : content;
+            this._contentEncoded = contentEncoded;
+            var callbacks = this._pendingContentCallbacks.slice();
+            for (var i = 0; i < callbacks.length; ++i)
+                callbacks[i](this._content, this._contentEncoded, this._mimeType);
+            this._pendingContentCallbacks.length = 0;
+            delete this._contentRequested;
+        }
+        NetworkAgent.getResponseBody(this._requestId, onResourceContent.bind(this));
+    },
+
+    /**
+     * @return {{type: WebInspector.NetworkRequest.InitiatorType, url: string, source: string, lineNumber: number, columnNumber: number}}
+     */
+    initiatorInfo: function()
+    {
+        if (this._initiatorInfo)
+            return this._initiatorInfo;
+
+        var type = WebInspector.NetworkRequest.InitiatorType.Other;
+        var url = "";
+        var lineNumber = -Infinity;
+        var columnNumber = -Infinity;
+
+        if (this.redirectSource) {
+            type = WebInspector.NetworkRequest.InitiatorType.Redirect;
+            url = this.redirectSource.url;
+        } else if (this.initiator) {
+            if (this.initiator.type === NetworkAgent.InitiatorType.Parser) {
+                type = WebInspector.NetworkRequest.InitiatorType.Parser;
+                url = this.initiator.url;
+                lineNumber = this.initiator.lineNumber;
+            } else if (this.initiator.type === NetworkAgent.InitiatorType.Script) {
+                var topFrame = this.initiator.stackTrace[0];
+                if (topFrame.url) {
+                    type = WebInspector.NetworkRequest.InitiatorType.Script;
+                    url = topFrame.url;
+                    lineNumber = topFrame.lineNumber;
+                    columnNumber = topFrame.columnNumber;
+                }
+            }
+        }
+
+        this._initiatorInfo = {type: type, url: url, source: WebInspector.displayNameForURL(url), lineNumber: lineNumber, columnNumber: columnNumber};
+        return this._initiatorInfo;
+    },
+
+    /**
+     * @return {!Array.<!Object>}
+     */
+    frames: function()
+    {
+        return this._frames;
+    },
+
+    /**
+     * @param {number} position
+     * @return {Object|undefined}
+     */
+    frame: function(position)
+    {
+        return this._frames[position];
+    },
+
+    /**
+     * @param {string} errorMessage
+     * @param {number} time
+     */
+    addFrameError: function(errorMessage, time)
+    {
+        this._pushFrame({errorMessage: errorMessage, time: time});
+    },
+
+    /**
+     * @param {!NetworkAgent.WebSocketFrame} response
+     * @param {number} time
+     * @param {boolean} sent
+     */
+    addFrame: function(response, time, sent)
+    {
+        response.time = time;
+        if (sent)
+            response.sent = sent;
+        this._pushFrame(response);
+    },
+
+    /**
+     * @param {!Object} frameOrError
+     */
+    _pushFrame: function(frameOrError)
+    {
+        if (this._frames.length >= 100)
+            this._frames.splice(0, 10);
+        this._frames.push(frameOrError);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
diff --git a/Source/devtools/front_end/NetworkUISourceCodeProvider.js b/Source/devtools/front_end/NetworkUISourceCodeProvider.js
new file mode 100644
index 0000000..2e40f86
--- /dev/null
+++ b/Source/devtools/front_end/NetworkUISourceCodeProvider.js
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.SimpleWorkspaceProvider} networkWorkspaceProvider
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.NetworkUISourceCodeProvider = function(networkWorkspaceProvider, workspace)
+{
+    this._networkWorkspaceProvider = networkWorkspaceProvider;
+    this._workspace = workspace;
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
+
+    this._processedURLs = {};
+    this._lastDynamicAnonymousScriptIndexForURL = {};
+}
+
+WebInspector.NetworkUISourceCodeProvider.prototype = {
+    _populate: function()
+    {
+        function populateFrame(frame)
+        {
+            for (var i = 0; i < frame.childFrames.length; ++i)
+                populateFrame.call(this, frame.childFrames[i]);
+
+            var resources = frame.resources();
+            for (var i = 0; i < resources.length; ++i)
+                this._resourceAdded({data:resources[i]});
+        }
+
+        populateFrame.call(this, WebInspector.resourceTreeModel.mainFrame);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _parsedScriptSource: function(event)
+    {
+        var script = /** @type {WebInspector.Script} */ (event.data);
+        if (!script.sourceURL || script.isInlineScript() || script.isSnippet())
+            return;
+        var isDynamicAnonymousScript;
+        // Only add uiSourceCodes for
+        // - content scripts;
+        // - scripts with explicit sourceURL comment;
+        // - dynamic scripts (script elements with src attribute) when inspector is opened after the script was loaded.
+        if (!script.hasSourceURL && !script.isContentScript) {
+            var requestURL = script.sourceURL.replace(/#.*/, "");
+            if (WebInspector.resourceForURL(requestURL) || WebInspector.networkLog.requestForURL(requestURL))
+                return;
+        }
+        // Filter out embedder injected content scripts.
+        if (script.isContentScript && !script.hasSourceURL) {
+            var parsedURL = new WebInspector.ParsedURL(script.sourceURL);
+            if (!parsedURL.host)
+                return;
+        }
+        this._addFile(script.sourceURL, script, script.isContentScript);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _resourceAdded: function(event)
+    {
+        var resource = /** @type {WebInspector.Resource} */ (event.data);
+        this._addFile(resource.url, resource);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _mainFrameNavigated: function(event)
+    {
+        this._reset();
+    },
+
+    /**
+     * @param {string} url
+     * @param {WebInspector.ContentProvider} contentProvider
+     * @param {boolean=} isContentScript
+     */
+    _addFile: function(url, contentProvider, isContentScript)
+    {
+        if (this._workspace.hasMappingForURL(url))
+            return;
+
+        var type = contentProvider.contentType();
+        if (type !== WebInspector.resourceTypes.Stylesheet && type !== WebInspector.resourceTypes.Document && type !== WebInspector.resourceTypes.Script)
+            return;
+        if (this._processedURLs[url])
+            return;
+        this._processedURLs[url] = true;
+        var isEditable = type !== WebInspector.resourceTypes.Document;
+        this._networkWorkspaceProvider.addFileForURL(url, contentProvider, isEditable, isContentScript);
+    },
+
+    _reset: function()
+    {
+        this._processedURLs = {};
+        this._lastDynamicAnonymousScriptIndexForURL = {};
+        this._networkWorkspaceProvider.reset();
+        this._populate();
+    }
+}
+
+/**
+ * @type {?WebInspector.SimpleWorkspaceProvider}
+ */
+WebInspector.networkWorkspaceProvider = null;
diff --git a/Source/devtools/front_end/OWNERS b/Source/devtools/front_end/OWNERS
new file mode 100644
index 0000000..501bd20
--- /dev/null
+++ b/Source/devtools/front_end/OWNERS
@@ -0,0 +1,6 @@
+apavlov@chromium.org
+caseq@chromium.org
+loislo@chromium.org
+pfeldman@chromium.org
+vsevik@chromium.org
+yurys@chromium.org
diff --git a/Source/devtools/front_end/Object.js b/Source/devtools/front_end/Object.js
new file mode 100644
index 0000000..01fff66
--- /dev/null
+++ b/Source/devtools/front_end/Object.js
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.EventTarget}
+ */
+WebInspector.Object = function() {
+}
+
+WebInspector.Object.prototype = {
+    /**
+     * @param {string} eventType
+     * @param {function(WebInspector.Event)} listener
+     * @param {Object=} thisObject
+     */
+    addEventListener: function(eventType, listener, thisObject)
+    {
+        console.assert(listener);
+
+        if (!this._listeners)
+            this._listeners = {};
+        if (!this._listeners[eventType])
+            this._listeners[eventType] = [];
+        this._listeners[eventType].push({ thisObject: thisObject, listener: listener });
+    },
+
+    /**
+     * @param {string} eventType
+     * @param {function(WebInspector.Event)} listener
+     * @param {Object=} thisObject
+     */
+    removeEventListener: function(eventType, listener, thisObject)
+    {
+        console.assert(listener);
+
+        if (!this._listeners || !this._listeners[eventType])
+            return;
+        var listeners = this._listeners[eventType];
+        for (var i = 0; i < listeners.length; ++i) {
+            if (listener && listeners[i].listener === listener && listeners[i].thisObject === thisObject)
+                listeners.splice(i, 1);
+            else if (!listener && thisObject && listeners[i].thisObject === thisObject)
+                listeners.splice(i, 1);
+        }
+
+        if (!listeners.length)
+            delete this._listeners[eventType];
+    },
+
+    removeAllListeners: function()
+    {
+        delete this._listeners;
+    },
+
+    /**
+     * @param {string} eventType
+     * @return {boolean}
+     */
+    hasEventListeners: function(eventType)
+    {
+        if (!this._listeners || !this._listeners[eventType])
+            return false;
+        return true;
+    },
+
+    /**
+     * @param {string} eventType
+     * @param {*=} eventData
+     * @return {boolean}
+     */
+    dispatchEventToListeners: function(eventType, eventData)
+    {
+        if (!this._listeners || !this._listeners[eventType])
+            return false;
+
+        var event = new WebInspector.Event(this, eventType, eventData);
+        var listeners = this._listeners[eventType].slice(0);
+        for (var i = 0; i < listeners.length; ++i) {
+            listeners[i].listener.call(listeners[i].thisObject, event);
+            if (event._stoppedPropagation)
+                break;
+        }
+
+        return event.defaultPrevented;
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.EventTarget} target
+ * @param {string} type
+ * @param {*=} data
+ */
+WebInspector.Event = function(target, type, data)
+{
+    this.target = target;
+    this.type = type;
+    this.data = data;
+    this.defaultPrevented = false;
+    this._stoppedPropagation = false;
+}
+
+WebInspector.Event.prototype = {
+    stopPropagation: function()
+    {
+        this._stoppedPropagation = true;
+    },
+
+    preventDefault: function()
+    {
+        this.defaultPrevented = true;
+    },
+
+    /**
+     * @param {boolean=} preventDefault
+     */
+    consume: function(preventDefault)
+    {
+        this.stopPropagation();
+        if (preventDefault)
+            this.preventDefault();
+    }
+}
+
+/**
+ * @interface
+ */
+WebInspector.EventTarget = function()
+{
+}
+
+WebInspector.EventTarget.prototype = {
+    /**
+     * @param {string} eventType
+     * @param {function(WebInspector.Event)} listener
+     * @param {Object=} thisObject
+     */
+    addEventListener: function(eventType, listener, thisObject) { },
+
+    /**
+     * @param {string} eventType
+     * @param {function(WebInspector.Event)} listener
+     * @param {Object=} thisObject
+     */
+    removeEventListener: function(eventType, listener, thisObject) { },
+
+    removeAllListeners: function() { },
+
+    /**
+     * @param {string} eventType
+     * @return {boolean}
+     */
+    hasEventListeners: function(eventType) { },
+
+    /**
+     * @param {string} eventType
+     * @param {*=} eventData
+     * @return {boolean}
+     */
+    dispatchEventToListeners: function(eventType, eventData) { },
+}
+
+WebInspector.notifications = new WebInspector.Object();
diff --git a/Source/devtools/front_end/ObjectPopoverHelper.js b/Source/devtools/front_end/ObjectPopoverHelper.js
new file mode 100644
index 0000000..9c32503
--- /dev/null
+++ b/Source/devtools/front_end/ObjectPopoverHelper.js
@@ -0,0 +1,161 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.PopoverHelper}
+ * @param {Element} panelElement
+ * @param {function(Element, Event):Element|undefined} getAnchor
+ * @param {function(Element, function(WebInspector.RemoteObject, boolean, Element=):undefined, string):undefined} queryObject
+ * @param {function()=} onHide
+ * @param {boolean=} disableOnClick
+ */
+WebInspector.ObjectPopoverHelper = function(panelElement, getAnchor, queryObject, onHide, disableOnClick)
+{
+    WebInspector.PopoverHelper.call(this, panelElement, getAnchor, this._showObjectPopover.bind(this), this._onHideObjectPopover.bind(this), disableOnClick);
+    this._queryObject = queryObject;
+    this._onHideCallback = onHide;
+    this._popoverObjectGroup = "popover";
+    panelElement.addEventListener("scroll", this.hidePopover.bind(this), true);
+};
+
+WebInspector.ObjectPopoverHelper.prototype = {
+    /**
+     * @param {Element} element
+     * @param {WebInspector.Popover} popover
+     */
+    _showObjectPopover: function(element, popover)
+    {
+        /**
+         * @param {WebInspector.RemoteObject} result
+         * @param {boolean} wasThrown
+         * @param {Element=} anchorOverride
+         */
+        function showObjectPopover(result, wasThrown, anchorOverride)
+        {
+            if (popover.disposed)
+                return;
+            if (wasThrown) {
+                this.hidePopover();
+                return;
+            }
+
+            var anchorElement = anchorOverride || element;
+
+            var popoverContentElement = null;
+            if (result.type !== "object") {
+                popoverContentElement = document.createElement("span");
+                popoverContentElement.className = "monospace console-formatted-" + result.type;
+                popoverContentElement.style.whiteSpace = "pre";
+                popoverContentElement.textContent = result.description;
+                if (result.type === "function") {
+                    function didGetDetails(error, response)
+                    {
+                        if (error) {
+                            console.error(error);
+                            return;
+                        }
+                        var container = document.createElement("div");
+                        container.className = "inline-block";
+
+                        var title = container.createChild("div", "function-popover-title source-code");
+                        var functionName = title.createChild("span", "function-name");
+                        functionName.textContent = response.name || response.inferredName || response.displayName || WebInspector.UIString("(anonymous function)");
+
+                        this._linkifier = new WebInspector.Linkifier();
+                        var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (response.location);
+                        var link = this._linkifier.linkifyRawLocation(rawLocation, "function-location-link");
+                        if (link)
+                            title.appendChild(link);
+
+                        container.appendChild(popoverContentElement);
+
+                        popover.show(container, anchorElement);
+                    }
+                    DebuggerAgent.getFunctionDetails(result.objectId, didGetDetails.bind(this));
+                    return;
+                }
+                if (result.type === "string")
+                    popoverContentElement.textContent = "\"" + popoverContentElement.textContent + "\"";
+                popover.show(popoverContentElement, anchorElement);
+            } else {
+                if (result.subtype === "node")
+                    result.highlightAsDOMNode();
+                popoverContentElement = document.createElement("div");
+                this._titleElement = document.createElement("div");
+                this._titleElement.className = "source-frame-popover-title monospace";
+                this._titleElement.textContent = result.description;
+                popoverContentElement.appendChild(this._titleElement);
+
+                var section = new WebInspector.ObjectPropertiesSection(result);
+                // For HTML DOM wrappers, append "#id" to title, if not empty.
+                if (result.description.substr(0, 4) === "HTML") {
+                    this._sectionUpdateProperties = section.updateProperties.bind(section);
+                    section.updateProperties = this._updateHTMLId.bind(this);
+                }
+                section.expanded = true;
+                section.element.addStyleClass("source-frame-popover-tree");
+                section.headerElement.addStyleClass("hidden");
+                popoverContentElement.appendChild(section.element);
+
+                const popoverWidth = 300;
+                const popoverHeight = 250;
+                popover.show(popoverContentElement, anchorElement, popoverWidth, popoverHeight);
+            }
+        }
+        this._queryObject(element, showObjectPopover.bind(this), this._popoverObjectGroup);
+    },
+
+    _onHideObjectPopover: function()
+    {
+        WebInspector.domAgent.hideDOMNodeHighlight();
+        if (this._linkifier) {
+            this._linkifier.reset();
+            delete this._linkifier;
+        }
+        if (this._onHideCallback)
+            this._onHideCallback();
+        RuntimeAgent.releaseObjectGroup(this._popoverObjectGroup);
+    },
+
+    _updateHTMLId: function(properties, rootTreeElementConstructor, rootPropertyComparer)
+    {
+        for (var i = 0; i < properties.length; ++i) {
+            if (properties[i].name === "id") {
+                if (properties[i].value.description)
+                    this._titleElement.textContent += "#" + properties[i].value.description;
+                break;
+            }
+        }
+        this._sectionUpdateProperties(properties, rootTreeElementConstructor, rootPropertyComparer);
+    },
+
+    __proto__: WebInspector.PopoverHelper.prototype
+}
diff --git a/Source/devtools/front_end/ObjectPropertiesSection.js b/Source/devtools/front_end/ObjectPropertiesSection.js
new file mode 100644
index 0000000..175a1c1
--- /dev/null
+++ b/Source/devtools/front_end/ObjectPropertiesSection.js
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 2008 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.PropertiesSection}
+ * @param {WebInspector.RemoteObject} object
+ * @param {?string|Element=} title
+ * @param {string=} subtitle
+ * @param {?string=} emptyPlaceholder
+ * @param {boolean=} ignoreHasOwnProperty
+ * @param {Array.<WebInspector.RemoteObjectProperty>=} extraProperties
+ * @param {function(new:TreeElement, WebInspector.RemoteObjectProperty)=} treeElementConstructor
+ */
+WebInspector.ObjectPropertiesSection = function(object, title, subtitle, emptyPlaceholder, ignoreHasOwnProperty, extraProperties, treeElementConstructor)
+{
+    this.emptyPlaceholder = (emptyPlaceholder || WebInspector.UIString("No Properties"));
+    this.object = object;
+    this.ignoreHasOwnProperty = ignoreHasOwnProperty;
+    this.extraProperties = extraProperties;
+    this.treeElementConstructor = treeElementConstructor || WebInspector.ObjectPropertyTreeElement;
+    this.editable = true;
+    this.skipProto = false;
+
+    WebInspector.PropertiesSection.call(this, title || "", subtitle);
+}
+
+WebInspector.ObjectPropertiesSection._arrayLoadThreshold = 100;
+
+WebInspector.ObjectPropertiesSection.prototype = {
+    enableContextMenu: function()
+    {
+        this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), false);
+    },
+
+    _contextMenuEventFired: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendApplicableItems(this.object);
+        contextMenu.show();
+    },
+
+    onpopulate: function()
+    {
+        this.update();
+    },
+
+    update: function()
+    {
+        if (this.object.arrayLength() > WebInspector.ObjectPropertiesSection._arrayLoadThreshold) {
+            this.propertiesTreeOutline.removeChildren();
+            WebInspector.ArrayGroupingTreeElement._populateArray(this.propertiesTreeOutline, this.object, 0, this.object.arrayLength() - 1);
+            return;
+        }
+
+        /**
+         * @param {Array.<WebInspector.RemoteObjectProperty>} properties
+         * @param {Array.<WebInspector.RemoteObjectProperty>=} internalProperties
+         */
+        function callback(properties, internalProperties)
+        {
+            if (!properties)
+                return;
+            this.updateProperties(properties, internalProperties);
+        }
+
+        if (this.ignoreHasOwnProperty)
+            this.object.getAllProperties(callback.bind(this));
+        else
+            this.object.getOwnProperties(callback.bind(this));
+    },
+
+    updateProperties: function(properties, internalProperties, rootTreeElementConstructor, rootPropertyComparer)
+    {
+        if (!rootTreeElementConstructor)
+            rootTreeElementConstructor = this.treeElementConstructor;
+
+        if (!rootPropertyComparer)
+            rootPropertyComparer = WebInspector.ObjectPropertiesSection.CompareProperties;
+
+        if (this.extraProperties) {
+            for (var i = 0; i < this.extraProperties.length; ++i)
+                properties.push(this.extraProperties[i]);
+        }
+
+        this.propertiesTreeOutline.removeChildren();
+
+        WebInspector.ObjectPropertyTreeElement.populateWithProperties(this.propertiesTreeOutline,
+            properties, internalProperties,
+            rootTreeElementConstructor, rootPropertyComparer,
+            this.skipProto, this.object);
+            
+        this.propertiesForTest = properties;
+
+        if (!this.propertiesTreeOutline.children.length) {
+            var title = document.createElement("div");
+            title.className = "info";
+            title.textContent = this.emptyPlaceholder;
+            var infoElement = new TreeElement(title, null, false);
+            this.propertiesTreeOutline.appendChild(infoElement);
+        }
+    },
+
+    __proto__: WebInspector.PropertiesSection.prototype
+}
+
+WebInspector.ObjectPropertiesSection.CompareProperties = function(propertyA, propertyB)
+{
+    var a = propertyA.name;
+    var b = propertyB.name;
+    if (a === "__proto__")
+        return 1;
+    if (b === "__proto__")
+        return -1;
+
+    // if used elsewhere make sure to
+    //  - convert a and b to strings (not needed here, properties are all strings)
+    //  - check if a == b (not needed here, no two properties can be the same)
+
+    var diff = 0;
+    var chunk = /^\d+|^\D+/;
+    var chunka, chunkb, anum, bnum;
+    while (diff === 0) {
+        if (!a && b)
+            return -1;
+        if (!b && a)
+            return 1;
+        chunka = a.match(chunk)[0];
+        chunkb = b.match(chunk)[0];
+        anum = !isNaN(chunka);
+        bnum = !isNaN(chunkb);
+        if (anum && !bnum)
+            return -1;
+        if (bnum && !anum)
+            return 1;
+        if (anum && bnum) {
+            diff = chunka - chunkb;
+            if (diff === 0 && chunka.length !== chunkb.length) {
+                if (!+chunka && !+chunkb) // chunks are strings of all 0s (special case)
+                    return chunka.length - chunkb.length;
+                else
+                    return chunkb.length - chunka.length;
+            }
+        } else if (chunka !== chunkb)
+            return (chunka < chunkb) ? -1 : 1;
+        a = a.substring(chunka.length);
+        b = b.substring(chunkb.length);
+    }
+    return diff;
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {WebInspector.RemoteObjectProperty} property
+ */
+WebInspector.ObjectPropertyTreeElement = function(property)
+{
+    this.property = property;
+
+    // Pass an empty title, the title gets made later in onattach.
+    TreeElement.call(this, "", null, false);
+    this.toggleOnClick = true;
+    this.selectable = false;
+}
+
+WebInspector.ObjectPropertyTreeElement.prototype = {
+    onpopulate: function()
+    {
+        return WebInspector.ObjectPropertyTreeElement.populate(this, this.property.value);
+    },
+
+    ondblclick: function(event)
+    {
+        if (this.property.writable)
+            this.startEditing(event);
+    },
+
+    onattach: function()
+    {
+        this.update();
+    },
+
+    update: function()
+    {
+        this.nameElement = document.createElement("span");
+        this.nameElement.className = "name";
+        this.nameElement.textContent = this.property.name;
+        if (!this.property.enumerable)
+            this.nameElement.addStyleClass("dimmed");
+
+        var separatorElement = document.createElement("span");
+        separatorElement.className = "separator";
+        separatorElement.textContent = ": ";
+
+        this.valueElement = document.createElement("span");
+        this.valueElement.className = "value";
+
+        var description = this.property.value.description;
+        // Render \n as a nice unicode cr symbol.
+        if (this.property.wasThrown)
+            this.valueElement.textContent = "[Exception: " + description + "]";
+        else if (this.property.value.type === "string" && typeof description === "string") {
+            this.valueElement.textContent = "\"" + description.replace(/\n/g, "\u21B5") + "\"";
+            this.valueElement._originalTextContent = "\"" + description + "\"";
+        } else if (this.property.value.type === "function" && typeof description === "string") {
+            this.valueElement.textContent = /.*/.exec(description)[0].replace(/ +$/g, "");
+            this.valueElement._originalTextContent = description;
+        } else if (this.property.value.type !== "object" || this.property.value.subtype !== "node") 
+            this.valueElement.textContent = description;
+
+        if (this.property.wasThrown)
+            this.valueElement.addStyleClass("error");
+        if (this.property.value.subtype)
+            this.valueElement.addStyleClass("console-formatted-" + this.property.value.subtype);
+        else if (this.property.value.type)
+            this.valueElement.addStyleClass("console-formatted-" + this.property.value.type);
+
+        this.valueElement.addEventListener("contextmenu", this._contextMenuFired.bind(this, this.property.value), false);
+        if (this.property.value.type === "object" && this.property.value.subtype === "node" && this.property.value.description) {
+            WebInspector.DOMPresentationUtils.createSpansForNodeTitle(this.valueElement, this.property.value.description);
+            this.valueElement.addEventListener("mousemove", this._mouseMove.bind(this, this.property.value), false);
+            this.valueElement.addEventListener("mouseout", this._mouseOut.bind(this, this.property.value), false);
+        } else
+            this.valueElement.title = description || "";
+
+        this.listItemElement.removeChildren();
+
+        this.listItemElement.appendChild(this.nameElement);
+        this.listItemElement.appendChild(separatorElement);
+        this.listItemElement.appendChild(this.valueElement);
+        this.hasChildren = this.property.value.hasChildren && !this.property.wasThrown;
+    },
+
+    _contextMenuFired: function(value, event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        this.populateContextMenu(contextMenu);
+        contextMenu.appendApplicableItems(value);
+        contextMenu.show();
+    },
+
+    /**
+     * @param {WebInspector.ContextMenu} contextMenu
+     */
+    populateContextMenu: function(contextMenu)
+    {
+    },
+
+    _mouseMove: function(event)
+    {
+        this.property.value.highlightAsDOMNode();
+    },
+
+    _mouseOut: function(event)
+    {
+        this.property.value.hideDOMNodeHighlight();
+    },
+
+    updateSiblings: function()
+    {
+        if (this.parent.root)
+            this.treeOutline.section.update();
+        else
+            this.parent.shouldRefreshChildren = true;
+    },
+
+    renderPromptAsBlock: function()
+    {
+        return false;
+    },
+
+    /**
+     * @param {Event=} event
+     */
+    elementAndValueToEdit: function(event)
+    {
+        return [this.valueElement, (typeof this.valueElement._originalTextContent === "string") ? this.valueElement._originalTextContent : undefined];
+    },
+
+    startEditing: function(event)
+    {
+        var elementAndValueToEdit = this.elementAndValueToEdit(event);
+        var elementToEdit = elementAndValueToEdit[0];
+        var valueToEdit = elementAndValueToEdit[1];
+
+        if (WebInspector.isBeingEdited(elementToEdit) || !this.treeOutline.section.editable || this._readOnly)
+            return;
+
+        // Edit original source.
+        if (typeof valueToEdit !== "undefined")
+            elementToEdit.textContent = valueToEdit;
+
+        var context = { expanded: this.expanded, elementToEdit: elementToEdit, previousContent: elementToEdit.textContent };
+
+        // Lie about our children to prevent expanding on double click and to collapse subproperties.
+        this.hasChildren = false;
+
+        this.listItemElement.addStyleClass("editing-sub-part");
+
+        this._prompt = new WebInspector.ObjectPropertyPrompt(this.editingCommitted.bind(this, null, elementToEdit.textContent, context.previousContent, context), this.editingCancelled.bind(this, null, context), this.renderPromptAsBlock());
+
+        function blurListener()
+        {
+            this.editingCommitted(null, elementToEdit.textContent, context.previousContent, context);
+        }
+
+        var proxyElement = this._prompt.attachAndStartEditing(elementToEdit, blurListener.bind(this));
+        window.getSelection().setBaseAndExtent(elementToEdit, 0, elementToEdit, 1);
+        proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this, context), false);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isEditing: function()
+    {
+        return !!this._prompt;
+    },
+
+    editingEnded: function(context)
+    {
+        this._prompt.detach();
+        delete this._prompt;
+
+        this.listItemElement.scrollLeft = 0;
+        this.listItemElement.removeStyleClass("editing-sub-part");
+        if (context.expanded)
+            this.expand();
+    },
+
+    editingCancelled: function(element, context)
+    {
+        this.editingEnded(context);
+        this.update();
+    },
+
+    editingCommitted: function(element, userInput, previousContent, context)
+    {
+        if (userInput === previousContent)
+            return this.editingCancelled(element, context); // nothing changed, so cancel
+
+        this.editingEnded(context);
+        this.applyExpression(userInput, true);
+    },
+
+    _promptKeyDown: function(context, event)
+    {
+        if (isEnterKey(event)) {
+            event.consume(true);
+            return this.editingCommitted(null, context.elementToEdit.textContent, context.previousContent, context);
+        }
+        if (event.keyIdentifier === "U+001B") { // Esc
+            event.consume();
+            return this.editingCancelled(null, context);
+        }
+    },
+
+    applyExpression: function(expression, updateInterface)
+    {
+        expression = expression.trim();
+        var expressionLength = expression.length;
+        function callback(error)
+        {
+            if (!updateInterface)
+                return;
+
+            if (error)
+                this.update();
+
+            if (!expressionLength) {
+                // The property was deleted, so remove this tree element.
+                this.parent.removeChild(this);
+            } else {
+                // Call updateSiblings since their value might be based on the value that just changed.
+                this.updateSiblings();
+            }
+        };
+        this.property.parentObject.setPropertyValue(this.property.name, expression.trim(), callback.bind(this));
+    },
+
+    propertyPath: function()
+    {
+        if ("_cachedPropertyPath" in this)
+            return this._cachedPropertyPath;
+
+        var current = this;
+        var result;
+
+        do {
+            if (current.property) {
+                if (result)
+                    result = current.property.name + "." + result;
+                else
+                    result = current.property.name;
+            }
+            current = current.parent;
+        } while (current && !current.root);
+
+        this._cachedPropertyPath = result;
+        return result;
+    },
+
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @param {TreeElement} treeElement
+ * @param {WebInspector.RemoteObject} value
+ */
+WebInspector.ObjectPropertyTreeElement.populate = function(treeElement, value) {
+    if (treeElement.children.length && !treeElement.shouldRefreshChildren)
+        return;
+
+    if (value.arrayLength() > WebInspector.ObjectPropertiesSection._arrayLoadThreshold) {
+        treeElement.removeChildren();
+        WebInspector.ArrayGroupingTreeElement._populateArray(treeElement, value, 0, value.arrayLength() - 1);
+        return;
+    }
+
+    /**
+     * @param {Array.<WebInspector.RemoteObjectProperty>} properties
+     * @param {Array.<WebInspector.RemoteObjectProperty>=} internalProperties
+     */
+    function callback(properties, internalProperties)
+    {
+        treeElement.removeChildren();
+        if (!properties)
+            return;
+        if (!internalProperties)
+            internalProperties = [];
+
+        WebInspector.ObjectPropertyTreeElement.populateWithProperties(treeElement, properties, internalProperties,
+            treeElement.treeOutline.section.treeElementConstructor, WebInspector.ObjectPropertiesSection.CompareProperties,
+            treeElement.treeOutline.section.skipProto, value);
+    }
+
+    value.getOwnProperties(callback);
+}
+
+/**
+ * @param {!TreeElement|!TreeOutline} treeElement
+ * @param {Array.<!WebInspector.RemoteObjectProperty>} properties
+ * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
+ * @param {function(new:TreeElement, WebInspector.RemoteObjectProperty)} treeElementConstructor
+ * @param {function (WebInspector.RemoteObjectProperty, WebInspector.RemoteObjectProperty): number} comparator
+ * @param {boolean} skipProto
+ * @param {?WebInspector.RemoteObject} value
+ */
+WebInspector.ObjectPropertyTreeElement.populateWithProperties = function(treeElement, properties, internalProperties, treeElementConstructor, comparator, skipProto, value) {
+    properties.sort(comparator);
+    
+    for (var i = 0; i < properties.length; ++i) {
+        if (skipProto && properties[i].name === "__proto__")
+            continue;
+        properties[i].parentObject = value;
+        treeElement.appendChild(new treeElementConstructor(properties[i]));
+    }
+    if (value && value.type === "function") {
+        // Whether function has TargetFunction internal property.
+        // This is a simple way to tell that the function is actually a bound function (we are not told).
+        // Bound function never has inner scope and doesn't need corresponding UI node.   
+        var hasTargetFunction = false;
+
+        if (internalProperties) {
+            for (var i = 0; i < internalProperties.length; i++) {
+                if (internalProperties[i].name == "[[TargetFunction]]") {
+                    hasTargetFunction = true;
+                    break;
+                }
+            }
+        }
+        if (!hasTargetFunction)
+            treeElement.appendChild(new WebInspector.FunctionScopeMainTreeElement(value));
+    }
+    if (internalProperties) {
+        for (var i = 0; i < internalProperties.length; i++) {
+            internalProperties[i].parentObject = value;
+            treeElement.appendChild(new treeElementConstructor(internalProperties[i]));
+        } 
+    }
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {WebInspector.RemoteObject} remoteObject
+ */
+WebInspector.FunctionScopeMainTreeElement = function(remoteObject)
+{
+    TreeElement.call(this, "<function scope>", null, false);
+    this.toggleOnClick = true;
+    this.selectable = false;
+    this._remoteObject = remoteObject;
+    this.hasChildren = true;
+}
+
+WebInspector.FunctionScopeMainTreeElement.prototype = {
+    onpopulate: function()
+    {
+        if (this.children.length && !this.shouldRefreshChildren)
+            return;
+
+        function didGetDetails(error, response)
+        {
+            if (error) {
+                console.error(error);
+                return;
+            }
+            this.removeChildren();
+
+            var scopeChain = response.scopeChain;
+            if (!scopeChain)
+                return;
+            for (var i = 0; i < scopeChain.length; ++i) {
+                var scope = scopeChain[i];
+                var title = null;
+                var isTrueObject;
+
+                switch (scope.type) {
+                    case "local":
+                        // Not really expecting this scope type here.
+                        title = WebInspector.UIString("Local");
+                        isTrueObject = false;
+                        break;
+                    case "closure":
+                        title = WebInspector.UIString("Closure");
+                        isTrueObject = false;
+                        break;
+                    case "catch":
+                        title = WebInspector.UIString("Catch");
+                        isTrueObject = false;
+                        break;
+                    case "with":
+                        title = WebInspector.UIString("With Block");
+                        isTrueObject = true;
+                        break;
+                    case "global":
+                        title = WebInspector.UIString("Global");
+                        isTrueObject = true;
+                        break;
+                    default:
+                        console.error("Unknown scope type: " + scope.type);
+                        continue;
+                }
+                
+                var scopeRef;
+                if (isTrueObject)
+                    scopeRef = undefined;
+                else 
+                    scopeRef = new WebInspector.ScopeRef(i, undefined, this._remoteObject.objectId);
+
+                var remoteObject = WebInspector.ScopeRemoteObject.fromPayload(scope.object, scopeRef);
+                if (isTrueObject) {
+                    var property = WebInspector.RemoteObjectProperty.fromScopeValue(title, remoteObject);
+                    property.parentObject = null;
+                    this.appendChild(new this.treeOutline.section.treeElementConstructor(property));
+                } else {
+                    var scopeTreeElement = new WebInspector.ScopeTreeElement(title, null, remoteObject);
+                    this.appendChild(scopeTreeElement);
+                }
+            }
+
+        }
+        DebuggerAgent.getFunctionDetails(this._remoteObject.objectId, didGetDetails.bind(this));
+    },
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {WebInspector.RemoteObject} remoteObject
+ */
+WebInspector.ScopeTreeElement = function(title, subtitle, remoteObject)
+{
+    // TODO: use subtitle parameter.
+    TreeElement.call(this, title, null, false);
+    this.toggleOnClick = true;
+    this.selectable = false;
+    this._remoteObject = remoteObject;
+    this.hasChildren = true;
+}
+
+WebInspector.ScopeTreeElement.prototype = {
+    onpopulate: function()
+    {
+        return WebInspector.ObjectPropertyTreeElement.populate(this, this._remoteObject);
+    },
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {WebInspector.RemoteObject} object
+ * @param {number} fromIndex
+ * @param {number} toIndex
+ * @param {number} propertyCount
+ */
+WebInspector.ArrayGroupingTreeElement = function(object, fromIndex, toIndex, propertyCount)
+{
+    TreeElement.call(this, String.sprintf("[%d \u2026 %d]", fromIndex, toIndex), undefined, true);
+    this._fromIndex = fromIndex;
+    this._toIndex = toIndex;
+    this._object = object;
+    this._readOnly = true;
+    this._propertyCount = propertyCount;
+    this._populated = false;
+}
+
+WebInspector.ArrayGroupingTreeElement._bucketThreshold = 100;
+WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold = 250000;
+
+/**
+ * @param {TreeElement|TreeOutline} treeElement
+ * @param {WebInspector.RemoteObject} object
+ * @param {number} fromIndex
+ * @param {number} toIndex
+ */
+WebInspector.ArrayGroupingTreeElement._populateArray = function(treeElement, object, fromIndex, toIndex)
+{
+    WebInspector.ArrayGroupingTreeElement._populateRanges(treeElement, object, fromIndex, toIndex, true);
+}
+
+/**
+ * @param {TreeElement|TreeOutline} treeElement
+ * @param {WebInspector.RemoteObject} object
+ * @param {number} fromIndex
+ * @param {number} toIndex
+ * @param {boolean} topLevel
+ */
+WebInspector.ArrayGroupingTreeElement._populateRanges = function(treeElement, object, fromIndex, toIndex, topLevel)
+{
+    object.callFunctionJSON(packRanges, [{value: fromIndex}, {value: toIndex}, {value: WebInspector.ArrayGroupingTreeElement._bucketThreshold}, {value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold}], callback.bind(this));
+
+    /**
+     * @this {Object}
+     * @param {number=} fromIndex // must declare optional
+     * @param {number=} toIndex // must declare optional
+     * @param {number=} bucketThreshold // must declare optional
+     * @param {number=} sparseIterationThreshold // must declare optional
+     */
+    function packRanges(fromIndex, toIndex, bucketThreshold, sparseIterationThreshold)
+    {
+        var ownPropertyNames = null;
+        function doLoop(iterationCallback)
+        {
+            if (toIndex - fromIndex < sparseIterationThreshold) {
+                for (var i = fromIndex; i <= toIndex; ++i) {
+                    if (i in this)
+                        iterationCallback(i);
+                }
+            } else {
+                ownPropertyNames = ownPropertyNames || Object.getOwnPropertyNames(this);
+                for (var i = 0; i < ownPropertyNames.length; ++i) {
+                    var name = ownPropertyNames[i];
+                    var index = name >>> 0;
+                    if (String(index) === name && fromIndex <= index && index <= toIndex)
+                        iterationCallback(index);
+                }
+            }
+        }
+
+        var count = 0;
+        function countIterationCallback()
+        {
+            ++count;
+        }
+        doLoop.call(this, countIterationCallback);
+
+        var bucketSize = count;
+        if (count <= bucketThreshold)
+            bucketSize = count;
+        else
+            bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.log(bucketThreshold)) - 1);
+
+        var ranges = [];
+        count = 0;
+        var groupStart = -1;
+        var groupEnd = 0;
+        function loopIterationCallback(i)
+        {
+            if (groupStart === -1)
+                groupStart = i;
+
+            groupEnd = i;
+            if (++count === bucketSize) {
+                ranges.push([groupStart, groupEnd, count]);
+                count = 0;
+                groupStart = -1;
+            }
+        }
+        doLoop.call(this, loopIterationCallback);
+
+        if (count > 0)
+            ranges.push([groupStart, groupEnd, count]);
+        return ranges;
+    }
+
+    function callback(ranges)
+    {
+        if (ranges.length == 1)
+            WebInspector.ArrayGroupingTreeElement._populateAsFragment(treeElement, object, ranges[0][0], ranges[0][1]);
+        else {
+            for (var i = 0; i < ranges.length; ++i) {
+                var fromIndex = ranges[i][0];
+                var toIndex = ranges[i][1];
+                var count = ranges[i][2];
+                if (fromIndex == toIndex)
+                    WebInspector.ArrayGroupingTreeElement._populateAsFragment(treeElement, object, fromIndex, toIndex);
+                else
+                    treeElement.appendChild(new WebInspector.ArrayGroupingTreeElement(object, fromIndex, toIndex, count));
+            }
+        }
+        if (topLevel)
+            WebInspector.ArrayGroupingTreeElement._populateNonIndexProperties(treeElement, object);
+    }
+}
+
+/**
+ * @param {TreeElement|TreeOutline} treeElement
+ * @param {WebInspector.RemoteObject} object
+ * @param {number} fromIndex
+ * @param {number} toIndex
+ */
+WebInspector.ArrayGroupingTreeElement._populateAsFragment = function(treeElement, object, fromIndex, toIndex)
+{
+    object.callFunction(buildArrayFragment, [{value: fromIndex}, {value: toIndex}, {value: WebInspector.ArrayGroupingTreeElement._sparseIterationThreshold}], processArrayFragment.bind(this));
+
+    /**
+     * @this {Object}
+     * @param {number=} fromIndex // must declare optional
+     * @param {number=} toIndex // must declare optional
+     * @param {number=} sparseIterationThreshold // must declare optional
+     */
+    function buildArrayFragment(fromIndex, toIndex, sparseIterationThreshold)
+    {
+        var result = Object.create(null);
+        if (toIndex - fromIndex < sparseIterationThreshold) {
+            for (var i = fromIndex; i <= toIndex; ++i) {
+                if (i in this)
+                    result[i] = this[i];
+            }
+        } else {
+            var ownPropertyNames = Object.getOwnPropertyNames(this);
+            for (var i = 0; i < ownPropertyNames.length; ++i) {
+                var name = ownPropertyNames[i];
+                var index = name >>> 0;
+                if (String(index) === name && fromIndex <= index && index <= toIndex)
+                    result[index] = this[index];
+            }
+        }
+        return result;
+    }
+
+    /** @this {WebInspector.ArrayGroupingTreeElement} */
+    function processArrayFragment(arrayFragment)
+    {
+        arrayFragment.getAllProperties(processProperties.bind(this));
+    }
+
+    /** @this {WebInspector.ArrayGroupingTreeElement} */
+    function processProperties(properties, internalProperties)
+    {
+        if (!properties)
+            return;
+
+        properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties);
+        for (var i = 0; i < properties.length; ++i) {
+            properties[i].parentObject = this._object;
+            var childTreeElement = new treeElement.treeOutline.section.treeElementConstructor(properties[i]);
+            childTreeElement._readOnly = true;
+            treeElement.appendChild(childTreeElement);
+        }
+    }
+}
+
+/**
+ * @param {TreeElement|TreeOutline} treeElement
+ * @param {WebInspector.RemoteObject} object
+ */
+WebInspector.ArrayGroupingTreeElement._populateNonIndexProperties = function(treeElement, object)
+{
+    object.callFunction(buildObjectFragment, undefined, processObjectFragment.bind(this));
+
+    /** @this {Object} */
+    function buildObjectFragment()
+    {
+        var result = Object.create(this.__proto__);
+        var names = Object.getOwnPropertyNames(this);
+        for (var i = 0; i < names.length; ++i) {
+            var name = names[i];
+            // Array index check according to the ES5-15.4.
+            if (String(name >>> 0) === name && name >>> 0 !== 0xffffffff)
+                continue;
+            var descriptor = Object.getOwnPropertyDescriptor(this, name);
+            if (descriptor)
+                Object.defineProperty(result, name, descriptor);
+        }
+        return result;
+    }
+
+    function processObjectFragment(arrayFragment)
+    {
+        arrayFragment.getOwnProperties(processProperties.bind(this));
+    }
+
+    /** @this {WebInspector.ArrayGroupingTreeElement} */
+    function processProperties(properties, internalProperties)
+    {
+        if (!properties)
+            return;
+
+        properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties);
+        for (var i = 0; i < properties.length; ++i) {
+            properties[i].parentObject = this._object;
+            var childTreeElement = new treeElement.treeOutline.section.treeElementConstructor(properties[i]);
+            childTreeElement._readOnly = true;
+            treeElement.appendChild(childTreeElement);
+        }
+    }
+}
+
+WebInspector.ArrayGroupingTreeElement.prototype = {
+    onpopulate: function()
+    {
+        if (this._populated)
+            return;
+
+        this._populated = true;
+
+        if (this._propertyCount >= WebInspector.ArrayGroupingTreeElement._bucketThreshold) {
+            WebInspector.ArrayGroupingTreeElement._populateRanges(this, this._object, this._fromIndex, this._toIndex, false);
+            return;
+        }
+        WebInspector.ArrayGroupingTreeElement._populateAsFragment(this, this._object, this._fromIndex, this._toIndex);
+    },
+
+    onattach: function()
+    {
+        this.listItemElement.addStyleClass("name");
+    },
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TextPrompt}
+ * @param {boolean=} renderAsBlock
+ */
+WebInspector.ObjectPropertyPrompt = function(commitHandler, cancelHandler, renderAsBlock)
+{
+    WebInspector.TextPrompt.call(this, WebInspector.runtimeModel.completionsForTextPrompt.bind(WebInspector.runtimeModel));
+    this.setSuggestBoxEnabled("generic-suggest");
+    if (renderAsBlock)
+        this.renderAsBlock();
+}
+
+WebInspector.ObjectPropertyPrompt.prototype = {
+    __proto__: WebInspector.TextPrompt.prototype
+}
diff --git a/Source/devtools/front_end/OverridesView.js b/Source/devtools/front_end/OverridesView.js
new file mode 100644
index 0000000..a184a36
--- /dev/null
+++ b/Source/devtools/front_end/OverridesView.js
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.OverridesView = function()
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("helpScreen.css");
+    this.element.addStyleClass("fill");
+    this.element.addStyleClass("help-window-main");
+    this.element.addStyleClass("settings-tab-container");
+
+    var paneContent = this.element.createChild("div", "tabbed-pane-content");
+
+    function appendBlockTo(targetElement, contentElement)
+    {
+        var blockElement = targetElement.createChild("div", "help-block");
+        blockElement.appendChild(contentElement);
+    }
+
+    var headerTitle = paneContent.createChild("header").createChild("h3");
+    headerTitle.appendChild(document.createTextNode(WebInspector.UIString("Overrides")));
+
+    var container = paneContent.createChild("div", "help-container-wrapper").createChild("div", "settings-tab help-content help-container");
+    this.containerElement = container;
+    appendBlockTo(container, this._createUserAgentControl());
+    appendBlockTo(container, this._createDeviceMetricsControl());
+    appendBlockTo(container, this._createGeolocationOverrideControl());
+    appendBlockTo(container, this._createDeviceOrientationOverrideControl());
+    appendBlockTo(container, this._createCheckboxSetting(WebInspector.UIString("Emulate touch events"), WebInspector.settings.emulateTouchEvents));
+    appendBlockTo(container, this._createMediaEmulationElement());
+
+    this._statusElement = document.createElement("span");
+    this._statusElement.textContent = WebInspector.UIString("Overrides");
+}
+
+WebInspector.OverridesView.showInDrawer = function()
+{
+    if (!WebInspector.OverridesView._view)
+        WebInspector.OverridesView._view = new WebInspector.OverridesView();
+    var view = WebInspector.OverridesView._view;
+    WebInspector.showViewInDrawer(view._statusElement, view);
+}
+
+WebInspector.OverridesView.prototype = {
+    /**
+     * @param {boolean=} omitParagraphElement
+     * @param {Element=} inputElement
+     */
+    _createCheckboxSetting: function(name, setting, omitParagraphElement, inputElement)
+    {
+        var input = inputElement || document.createElement("input");
+        input.type = "checkbox";
+        input.name = name;
+        input.checked = setting.get();
+
+        function listener()
+        {
+            setting.set(input.checked);
+        }
+        input.addEventListener("click", listener, false);
+
+        var label = document.createElement("label");
+        label.appendChild(input);
+        label.appendChild(document.createTextNode(name));
+        if (omitParagraphElement)
+            return label;
+
+        var p = document.createElement("p");
+        p.appendChild(label);
+        return p;
+    },
+
+    _createUserAgentControl: function()
+    {
+        var userAgent = WebInspector.settings.userAgent.get();
+
+        var p = document.createElement("p");
+        var labelElement = p.createChild("label");
+        var checkboxElement = labelElement.createChild("input");
+        checkboxElement.type = "checkbox";
+        checkboxElement.checked = false;
+        labelElement.appendChild(document.createTextNode(WebInspector.UIString("User Agent")));
+        p.appendChild(this._createUserAgentSelectRowElement(checkboxElement));
+        return p;
+    },
+
+    _createUserAgentSelectRowElement: function(checkboxElement)
+    {
+        var userAgent = WebInspector.settings.userAgent.get();
+
+        // When present, the third element lists device metrics separated by 'x':
+        // - screen width,
+        // - screen height,
+        // - font scale factor.
+        const userAgents = [
+            ["Internet Explorer 9", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"],
+            ["Internet Explorer 8", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)"],
+            ["Internet Explorer 7", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"],
+
+            ["Firefox 7 \u2014 Windows", "Mozilla/5.0 (Windows NT 6.1; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1"],
+            ["Firefox 7 \u2014 Mac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1"],
+            ["Firefox 4 \u2014 Windows", "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"],
+            ["Firefox 4 \u2014 Mac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"],
+            ["Firefox 14 \u2014 Android Mobile", "Mozilla/5.0 (Android; Mobile; rv:14.0) Gecko/14.0 Firefox/14.0"],
+            ["Firefox 14 \u2014 Android Tablet", "Mozilla/5.0 (Android; Tablet; rv:14.0) Gecko/14.0 Firefox/14.0"],
+
+            ["Chrome \u2014 Android Mobile", "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"],
+            ["Chrome \u2014 Android Tablet", "Mozilla/5.0 (Linux; Android 4.1.2; Nexus 7 Build/JZ054K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19"],
+
+            ["iPhone \u2014 iOS 5", "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", "640x960x1"],
+            ["iPhone \u2014 iOS 4", "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_2 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8H7 Safari/6533.18.5", "640x960x1"],
+            ["iPad \u2014 iOS 5", "Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", "1024x768x1"],
+            ["iPad \u2014 iOS 4", "Mozilla/5.0 (iPad; CPU OS 4_3_2 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8H7 Safari/6533.18.5", "1024x768x1"],
+
+            ["Android 2.3 \u2014 Nexus S", "Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus S Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", "480x800x1.1"],
+            ["Android 4.0.2 \u2014 Galaxy Nexus", "Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", "720x1280x1.1"],
+
+            ["BlackBerry \u2014 PlayBook 2.1", "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML, like Gecko) Version/7.2.1.0 Safari/536.2+", "1024x600x1"],
+            ["BlackBerry \u2014 9900", "Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0.187 Mobile Safari/534.11+", "640x480x1"],
+            ["BlackBerry \u2014 BB10", "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.1+ (KHTML, like Gecko) Version/10.0.0.1337 Mobile Safari/537.1+", "768x1280x1"],
+
+            ["MeeGo \u2014 Nokia N9", "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", "480x854x1"],
+
+            [WebInspector.UIString("Other..."), "Other"]
+        ];
+
+        var fieldsetElement = document.createElement("fieldset");
+        this._selectElement = fieldsetElement.createChild("select");
+        this._otherUserAgentElement = fieldsetElement.createChild("input");
+        this._otherUserAgentElement.type = "text";
+        this._otherUserAgentElement.value = userAgent;
+        this._otherUserAgentElement.title = userAgent;
+        this._userAgentFieldsetElement = fieldsetElement;
+
+        var selectionRestored = false;
+        for (var i = 0; i < userAgents.length; ++i) {
+            var agent = userAgents[i];
+            var option = new Option(agent[0], agent[1]);
+            option._metrics = agent[2] ? agent[2] : "";
+            this._selectElement.add(option);
+            if (userAgent === agent[1]) {
+                this._selectElement.selectedIndex = i;
+                selectionRestored = true;
+            }
+        }
+
+        if (!selectionRestored) {
+            if (!userAgent)
+                this._selectElement.selectedIndex = 0;
+            else
+                this._selectElement.selectedIndex = userAgents.length - 1;
+        }
+
+        this._selectElement.addEventListener("change", this._selectionChanged.bind(this, true), false);
+
+        fieldsetElement.addEventListener("dblclick", textDoubleClicked.bind(this), false);
+        this._otherUserAgentElement.addEventListener("blur", textChanged.bind(this), false);
+
+        function textDoubleClicked()
+        {
+            this._selectElement.selectedIndex = userAgents.length - 1;
+            this._selectionChanged();
+        }
+
+        function textChanged()
+        {
+            WebInspector.settings.userAgent.set(this._otherUserAgentElement.value);
+        }
+
+        function checkboxClicked()
+        {
+            if (checkboxElement.checked) {
+                this._userAgentFieldsetElement.disabled = false;
+                this._selectionChanged();
+            } else {
+                this._userAgentFieldsetElement.disabled = true;
+                this._otherUserAgentElement.disabled = true;
+            }
+            WebInspector.userAgentSupport.toggleUserAgentOverride(checkboxElement.checked);
+        }
+        checkboxElement.addEventListener("click", checkboxClicked.bind(this), false);
+
+        checkboxClicked.call(this);
+        return fieldsetElement;
+    },
+
+    /**
+     * @param {boolean=} isUserGesture
+     */
+    _selectionChanged: function(isUserGesture)
+    {
+        var value = this._selectElement.options[this._selectElement.selectedIndex].value;
+        if (value !== "Other") {
+            WebInspector.settings.userAgent.set(value);
+            this._otherUserAgentElement.value = value;
+            this._otherUserAgentElement.title = value;
+            this._otherUserAgentElement.disabled = true;
+        } else {
+            this._otherUserAgentElement.disabled = false;
+            this._otherUserAgentElement.focus();
+        }
+
+        if (isUserGesture) {
+            var metrics = this._selectElement.options[this._selectElement.selectedIndex]._metrics;
+            this._setDeviceMetricsOverride(WebInspector.UserAgentSupport.DeviceMetrics.parseSetting(metrics), false, true);
+        }
+    },
+
+    /**
+     * Creates an input element under the parentElement with the given id and defaultText.
+     * It also sets an onblur event listener.
+     * @param {Element} parentElement
+     * @param {string} id
+     * @param {string} defaultText
+     * @param {function(*)} eventListener
+     * @param {boolean=} numeric
+     * @return {Element} element
+     */
+    _createInput: function(parentElement, id, defaultText, eventListener, numeric)
+    {
+        var element = parentElement.createChild("input");
+        element.id = id;
+        element.type = "text";
+        element.maxLength = 12;
+        element.style.width = "80px";
+        element.value = defaultText;
+        element.align = "right";
+        if (numeric)
+            element.className = "numeric";
+        element.addEventListener("blur", eventListener, false);
+        return element;
+    },
+
+    _createDeviceMetricsControl: function()
+    {
+        const metricsSetting = WebInspector.settings.deviceMetrics.get();
+        var metrics = WebInspector.UserAgentSupport.DeviceMetrics.parseSetting(metricsSetting);
+
+        const p = document.createElement("p");
+        const labelElement = p.createChild("label");
+        const checkboxElement = labelElement.createChild("input");
+        checkboxElement.id = "metrics-override-checkbox";
+        checkboxElement.type = "checkbox";
+        checkboxElement.checked = false;
+        checkboxElement.addEventListener("click", this._onMetricsCheckboxClicked.bind(this), false);
+        this._metricsCheckboxElement = checkboxElement;
+        labelElement.appendChild(document.createTextNode(WebInspector.UIString("Device metrics")));
+
+        const metricsSectionElement = this._createDeviceMetricsElement(metrics);
+        p.appendChild(metricsSectionElement);
+        this._metricsSectionElement = metricsSectionElement;
+        this._onMetricsCheckboxClicked();
+
+        return p;
+    },
+
+    _onMetricsCheckboxClicked: function()
+    {
+        var controlsDisabled = !this._metricsCheckboxElement.checked;
+        this._deviceMetricsFieldsetElement.disabled = controlsDisabled;
+
+        if (controlsDisabled) {
+            WebInspector.userAgentSupport.toggleDeviceMetricsOverride(false);
+            return;
+        }
+
+        var metrics = WebInspector.UserAgentSupport.DeviceMetrics.parseUserInput(this._widthOverrideElement.value, this._heightOverrideElement.value, this._fontScaleFactorOverrideElement.value);
+        if (metrics && metrics.isValid() && metrics.width && metrics.height) {
+            this._setDeviceMetricsOverride(metrics, false, false);
+            WebInspector.userAgentSupport.toggleDeviceMetricsOverride(true);
+        }
+        if (!this._widthOverrideElement.value)
+            this._widthOverrideElement.focus();
+    },
+
+    _applyDeviceMetricsUserInput: function()
+    {
+        this._setDeviceMetricsOverride(WebInspector.UserAgentSupport.DeviceMetrics.parseUserInput(this._widthOverrideElement.value.trim(), this._heightOverrideElement.value.trim(), this._fontScaleFactorOverrideElement.value.trim()), true, false);
+    },
+
+    /**
+     * @param {?WebInspector.UserAgentSupport.DeviceMetrics} metrics
+     * @param {boolean} userInputModified
+     */
+    _setDeviceMetricsOverride: function(metrics, userInputModified, updateCheckbox)
+    {
+        function setValid(condition, element)
+        {
+            if (condition)
+                element.removeStyleClass("error-input");
+            else
+                element.addStyleClass("error-input");
+        }
+
+        setValid(metrics && metrics.isWidthValid(), this._widthOverrideElement);
+        setValid(metrics && metrics.isHeightValid(), this._heightOverrideElement);
+        setValid(metrics && metrics.isFontScaleFactorValid(), this._fontScaleFactorOverrideElement);
+
+        if (!metrics)
+            return;
+
+        if (!userInputModified) {
+            this._widthOverrideElement.value = metrics.widthToInput();
+            this._heightOverrideElement.value = metrics.heightToInput();
+            this._fontScaleFactorOverrideElement.value = metrics.fontScaleFactorToInput();
+        }
+
+        if (metrics.isValid()) {
+            var value = metrics.toSetting();
+            if (value !== WebInspector.settings.deviceMetrics.get())
+                WebInspector.settings.deviceMetrics.set(value);
+        }
+
+        if (this._metricsCheckboxElement && updateCheckbox) {
+            this._metricsCheckboxElement.checked = !!metrics.toSetting();
+            this._onMetricsCheckboxClicked();
+        }
+    },
+
+    /**
+     * @param {WebInspector.UserAgentSupport.DeviceMetrics} metrics
+     */
+    _createDeviceMetricsElement: function(metrics)
+    {
+        var fieldsetElement = document.createElement("fieldset");
+        fieldsetElement.id = "metrics-override-section";
+        this._deviceMetricsFieldsetElement = fieldsetElement;
+
+        function swapDimensionsClicked(event)
+        {
+            var widthValue = this._widthOverrideElement.value;
+            this._widthOverrideElement.value = this._heightOverrideElement.value;
+            this._heightOverrideElement.value = widthValue;
+            this._applyDeviceMetricsUserInput();
+        }
+
+        var tableElement = fieldsetElement.createChild("table", "nowrap");
+
+        var rowElement = tableElement.createChild("tr");
+        var cellElement = rowElement.createChild("td");
+        cellElement.appendChild(document.createTextNode(WebInspector.UIString("Screen resolution:")));
+        cellElement = rowElement.createChild("td");
+        this._widthOverrideElement = this._createInput(cellElement, "metrics-override-width", String(metrics.width || screen.width), this._applyDeviceMetricsUserInput.bind(this), true);
+        cellElement.appendChild(document.createTextNode(" \u00D7 ")); // MULTIPLICATION SIGN.
+        this._heightOverrideElement = this._createInput(cellElement, "metrics-override-height", String(metrics.height || screen.height), this._applyDeviceMetricsUserInput.bind(this), true);
+        cellElement.appendChild(document.createTextNode(" \u2014 ")); // EM DASH.
+        this._swapDimensionsElement = cellElement.createChild("button");
+        this._swapDimensionsElement.appendChild(document.createTextNode(" \u21C4 ")); // RIGHTWARDS ARROW OVER LEFTWARDS ARROW.
+        this._swapDimensionsElement.title = WebInspector.UIString("Swap dimensions");
+        this._swapDimensionsElement.addEventListener("click", swapDimensionsClicked.bind(this), false);
+
+        rowElement = tableElement.createChild("tr");
+        cellElement = rowElement.createChild("td");
+        cellElement.appendChild(document.createTextNode(WebInspector.UIString("Font scale factor:")));
+        cellElement = rowElement.createChild("td");
+        this._fontScaleFactorOverrideElement = this._createInput(cellElement, "metrics-override-font-scale", String(metrics.fontScaleFactor || 1), this._applyDeviceMetricsUserInput.bind(this), true);
+
+        rowElement = tableElement.createChild("tr");
+        cellElement = rowElement.createChild("td");
+        cellElement.colSpan = 2;
+        this._fitWindowCheckboxElement = document.createElement("input");
+        cellElement.appendChild(this._createCheckboxSetting(WebInspector.UIString("Fit in window"), WebInspector.settings.deviceFitWindow, true, this._fitWindowCheckboxElement));
+
+        return fieldsetElement;
+    },
+
+    _createGeolocationOverrideControl: function()
+    {
+        const geolocationSetting = WebInspector.settings.geolocationOverride.get();
+        var geolocation = WebInspector.UserAgentSupport.GeolocationPosition.parseSetting(geolocationSetting);
+        var p = document.createElement("p");
+        var labelElement = p.createChild("label");
+        var checkboxElement = labelElement.createChild("input");
+        checkboxElement.id = "geolocation-override-checkbox";
+        checkboxElement.type = "checkbox";
+        checkboxElement.checked = false;
+        checkboxElement.addEventListener("click", this._onGeolocationOverrideCheckboxClicked.bind(this), false);
+        this._geolocationOverrideCheckboxElement = checkboxElement;
+        labelElement.appendChild(document.createTextNode(WebInspector.UIString("Override Geolocation")));
+
+        var geolocationSectionElement = this._createGeolocationOverrideElement(geolocation);
+        p.appendChild(geolocationSectionElement);
+        this._geolocationSectionElement = geolocationSectionElement;
+        this._onGeolocationOverrideCheckboxClicked();
+        return p;
+    },
+
+    _onGeolocationOverrideCheckboxClicked: function()
+    {
+        var controlsDisabled = !this._geolocationOverrideCheckboxElement.checked;
+        this._geolocationFieldsetElement.disabled = controlsDisabled;
+
+        if (controlsDisabled) {
+            WebInspector.userAgentSupport.toggleGeolocationPositionOverride(false);
+            return;
+        }
+
+        var geolocation = WebInspector.UserAgentSupport.GeolocationPosition.parseUserInput(this._latitudeElement.value, this._longitudeElement.value, this._geolocationErrorElement.checked);
+        if (geolocation) {
+            this._setGeolocationPosition(geolocation, false, false);
+            WebInspector.userAgentSupport.toggleGeolocationPositionOverride(true);
+        }
+        if (!this._latitudeElement.value)
+            this._latitudeElement.focus();
+    },
+
+    _applyGeolocationUserInput: function()
+    {
+        this._setGeolocationPosition(WebInspector.UserAgentSupport.GeolocationPosition.parseUserInput(this._latitudeElement.value.trim(), this._longitudeElement.value.trim(), this._geolocationErrorElement.checked), true, false);
+    },
+
+    /**
+     * @param {?WebInspector.UserAgentSupport.GeolocationPosition} geolocation
+     * @param {boolean} userInputModified
+     * @param {boolean} updateCheckbox
+     */
+    _setGeolocationPosition: function(geolocation, userInputModified, updateCheckbox)
+    {
+        if (!geolocation)
+            return;
+
+        if (!userInputModified) {
+            this._latitudeElement.value = geolocation.latitude;
+            this._longitudeElement.value = geolocation.longitude;
+        }
+
+        var value = geolocation.toSetting();
+        WebInspector.settings.geolocationOverride.set(value);
+
+        if (this._geolocationOverrideCheckboxElement && updateCheckbox) {
+            this._geolocationOverrideCheckboxElement.checked = !!geolocation.toSetting();
+            this._onGeolocationOverrideCheckboxClicked();
+        }
+    },
+
+    /**
+     * @param {WebInspector.UserAgentSupport.GeolocationPosition} geolocation
+     */
+    _createGeolocationOverrideElement: function(geolocation)
+    {
+        var fieldsetElement = document.createElement("fieldset");
+        fieldsetElement.id = "geolocation-override-section";
+        this._geolocationFieldsetElement = fieldsetElement;
+
+        var tableElement = fieldsetElement.createChild("table");
+        var rowElement = tableElement.createChild("tr");
+        var cellElement = rowElement.createChild("td");
+        cellElement.appendChild(document.createTextNode(WebInspector.UIString("Geolocation Position") + ":"));
+        cellElement = rowElement.createChild("td");
+        cellElement.appendChild(document.createTextNode(WebInspector.UIString("Lat = ")));
+        this._latitudeElement = this._createInput(cellElement, "geolocation-override-latitude", String(geolocation.latitude), this._applyGeolocationUserInput.bind(this), true);
+        cellElement.appendChild(document.createTextNode(" , "));
+        cellElement.appendChild(document.createTextNode(WebInspector.UIString("Lon = ")));
+        this._longitudeElement = this._createInput(cellElement, "geolocation-override-longitude", String(geolocation.longitude), this._applyGeolocationUserInput.bind(this), true);
+        rowElement = tableElement.createChild("tr");
+        cellElement = rowElement.createChild("td");
+        cellElement.colSpan = 2;
+        var geolocationErrorLabelElement = document.createElement("label");
+        var geolocationErrorCheckboxElement = geolocationErrorLabelElement.createChild("input");
+        geolocationErrorCheckboxElement.id = "geolocation-error";
+        geolocationErrorCheckboxElement.type = "checkbox";
+        geolocationErrorCheckboxElement.checked = !geolocation || geolocation.error;
+        geolocationErrorCheckboxElement.addEventListener("click", this._applyGeolocationUserInput.bind(this), false);
+        geolocationErrorLabelElement.appendChild(document.createTextNode(WebInspector.UIString("Emulate position unavailable")));
+        this._geolocationErrorElement = geolocationErrorCheckboxElement;
+        cellElement.appendChild(geolocationErrorLabelElement);
+
+        return fieldsetElement;
+    },
+
+    _createDeviceOrientationOverrideControl: function()
+    {
+        const deviceOrientationSetting = WebInspector.settings.deviceOrientationOverride.get();
+        var deviceOrientation = WebInspector.UserAgentSupport.DeviceOrientation.parseSetting(deviceOrientationSetting);
+
+        var p = document.createElement("p");
+        var labelElement = p.createChild("label");
+        var checkboxElement = labelElement.createChild("input");
+        checkboxElement.id = "device-orientation-override-checkbox";
+        checkboxElement.type = "checkbox";
+        checkboxElement.checked = false;
+        checkboxElement.addEventListener("click", this._onDeviceOrientationOverrideCheckboxClicked.bind(this), false);
+        this._deviceOrientationOverrideCheckboxElement = checkboxElement;
+        labelElement.appendChild(document.createTextNode(WebInspector.UIString("Override Device Orientation")));
+
+        var deviceOrientationSectionElement = this._createDeviceOrientationOverrideElement(deviceOrientation);
+        p.appendChild(deviceOrientationSectionElement);
+        this._deviceOrientationSectionElement = deviceOrientationSectionElement;
+        this._onDeviceOrientationOverrideCheckboxClicked();
+        return p;
+    },
+
+    _onDeviceOrientationOverrideCheckboxClicked: function()
+    {
+        var controlsDisabled = !this._deviceOrientationOverrideCheckboxElement.checked;
+        this._deviceOrientationFieldsetElement.disabled = controlsDisabled;
+
+        if (controlsDisabled) {
+            WebInspector.userAgentSupport.toggleDeviceOrientationOverride(false);
+            return;
+        }
+
+        var deviceOrientation = WebInspector.UserAgentSupport.DeviceOrientation.parseUserInput(this._alphaElement.value, this._betaElement.value, this._gammaElement.value);
+        if (deviceOrientation) {
+            this._setDeviceOrientation(deviceOrientation, false, false);
+            WebInspector.userAgentSupport.toggleDeviceOrientationOverride(true);
+        }
+        if (!this._alphaElement.value)
+            this._alphaElement.focus();
+    },
+
+    _applyDeviceOrientationUserInput: function()
+    {
+        this._setDeviceOrientation(WebInspector.UserAgentSupport.DeviceOrientation.parseUserInput(this._alphaElement.value.trim(), this._betaElement.value.trim(), this._gammaElement.value.trim()), true, false);
+    },
+
+    /**
+     * @param {?WebInspector.UserAgentSupport.DeviceOrientation} deviceOrientation
+     * @param {boolean} userInputModified
+     * @param {boolean} updateCheckbox
+     */
+    _setDeviceOrientation: function(deviceOrientation, userInputModified, updateCheckbox)
+    {
+        if (!deviceOrientation)
+            return;
+
+        if (!userInputModified) {
+            this._alphaElement.value = deviceOrientation.alpha;
+            this._betaElement.value = deviceOrientation.beta;
+            this._gammaElement.value = deviceOrientation.gamma;
+        }
+
+        var value = deviceOrientation.toSetting();
+        WebInspector.settings.deviceOrientationOverride.set(value);
+
+        if (this._deviceOrientationOverrideCheckboxElement && updateCheckbox) {
+            this._deviceOrientationOverrideCheckboxElement.checked = !!deviceOrientation.toSetting();
+            this._onDeviceOrientationOverrideCheckboxClicked();
+        }
+    },
+
+    /**
+     * @param {WebInspector.UserAgentSupport.DeviceOrientation} deviceOrientation
+     */
+    _createDeviceOrientationOverrideElement: function(deviceOrientation)
+    {
+        var fieldsetElement = document.createElement("fieldset");
+        fieldsetElement.id = "device-orientation-override-section";
+        this._deviceOrientationFieldsetElement = fieldsetElement;
+
+        var tableElement = fieldsetElement.createChild("table");
+
+        var rowElement = tableElement.createChild("tr");
+        var cellElement = rowElement.createChild("td");
+        cellElement.appendChild(document.createTextNode("\u03B1: "));
+        this._alphaElement = this._createInput(cellElement, "device-orientation-override-alpha", String(deviceOrientation.alpha), this._applyDeviceOrientationUserInput.bind(this), true);
+        cellElement.appendChild(document.createTextNode(" \u03B2: "));
+        this._betaElement = this._createInput(cellElement, "device-orientation-override-beta", String(deviceOrientation.beta), this._applyDeviceOrientationUserInput.bind(this), true);
+        cellElement.appendChild(document.createTextNode(" \u03B3: "));
+        this._gammaElement = this._createInput(cellElement, "device-orientation-override-gamma", String(deviceOrientation.gamma), this._applyDeviceOrientationUserInput.bind(this), true);
+
+        return fieldsetElement;
+    },
+
+    _createMediaEmulationElement: function()
+    {
+        const p = document.createElement("p");
+        const labelElement = p.createChild("label");
+        const checkboxElement = labelElement.createChild("input");
+        checkboxElement.type = "checkbox";
+        checkboxElement.checked = false;
+        labelElement.appendChild(document.createTextNode(WebInspector.UIString("Emulate CSS media")));
+
+        var mediaSelectElement = p.createChild("select");
+        var mediaTypes = WebInspector.CSSStyleModel.MediaTypes;
+        var defaultMedia = WebInspector.settings.emulatedCSSMedia.get();
+        for (var i = 0; i < mediaTypes.length; ++i) {
+            var mediaType = mediaTypes[i];
+            if (mediaType === "all") {
+                // "all" is not a device-specific media type.
+                continue;
+            }
+            var option = document.createElement("option");
+            option.text = mediaType;
+            option.value = mediaType;
+            mediaSelectElement.add(option);
+            if (mediaType === defaultMedia)
+                mediaSelectElement.selectedIndex = mediaSelectElement.options.length - 1;
+        }
+        mediaSelectElement.disabled = true;
+        var boundListener = this._emulateMediaChanged.bind(this, checkboxElement, mediaSelectElement);
+        checkboxElement.addEventListener("click", boundListener, false);
+        mediaSelectElement.addEventListener("change", boundListener, false);
+        return p;
+    },
+
+    _emulateMediaChanged: function(checkbox, select)
+    {
+        select.disabled = !checkbox.checked;
+        if (checkbox.checked) {
+            var media = select.options[select.selectedIndex].value;
+            WebInspector.settings.emulatedCSSMedia.set(media);
+            PageAgent.setEmulatedMedia(media);
+        } else
+            PageAgent.setEmulatedMedia("");
+        WebInspector.cssModel.mediaQueryResultChanged();
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/OverviewGrid.js b/Source/devtools/front_end/OverviewGrid.js
new file mode 100644
index 0000000..c10e878
--- /dev/null
+++ b/Source/devtools/front_end/OverviewGrid.js
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {string} prefix
+ */
+WebInspector.OverviewGrid = function(prefix)
+{
+    this.element = document.createElement("div");
+    this.element.className = "fill";
+    this.element.id = prefix + "-overview-container";
+
+    this._grid = new WebInspector.TimelineGrid();
+    this._grid.element.id = prefix + "-overview-grid";
+    this._grid.setScrollAndDividerTop(0, 0);
+
+    this.element.appendChild(this._grid.element);
+
+    this._window = new WebInspector.OverviewGrid.Window(this.element, this._grid.dividersLabelBarElement);
+}
+
+WebInspector.OverviewGrid.prototype = {
+    /**
+     * @return {number}
+     */
+    clientWidth: function()
+    {
+        return this.element.clientWidth;
+    },
+
+    /**
+     * @param {!WebInspector.TimelineGrid.Calculator} calculator
+     */
+    updateDividers: function(calculator)
+    {
+        this._grid.updateDividers(calculator);
+    },
+
+    /**
+     * @param {!Array.<Element>} dividers
+     */
+    addEventDividers: function(dividers)
+    {
+        this._grid.addEventDividers(dividers);
+    },
+
+    removeEventDividers: function()
+    {
+        this._grid.removeEventDividers();
+    },
+
+    /**
+     * @param {?number} start
+     * @param {?number} end
+     */
+    setWindowPosition: function(start, end)
+    {
+        this._window._setWindowPosition(start, end);
+    },
+
+    reset: function()
+    {
+        this._window.reset();
+    },
+
+    /**
+     * @return {number}
+     */
+    windowLeft: function()
+    {
+        return this._window.windowLeft;
+    },
+
+    /**
+     * @return {number}
+     */
+    windowRight: function()
+    {
+        return this._window.windowRight;
+    },
+
+    /**
+     * @param {number} left
+     * @param {number} right
+     */
+    setWindow: function(left, right)
+    {
+        this._window._setWindow(left, right);
+    },
+
+    /**
+     * @param {string} eventType
+     * @param {function(WebInspector.Event)} listener
+     * @param {Object=} thisObject
+     */
+    addEventListener: function(eventType, listener, thisObject)
+    {
+        this._window.addEventListener(eventType, listener, thisObject);
+    },
+
+    /**
+     * @param {!number} zoomFactor
+     * @param {!number} referencePoint
+     */
+    zoom: function(zoomFactor, referencePoint)
+    {
+        this._window._zoom(zoomFactor, referencePoint);
+    }
+}
+
+
+WebInspector.OverviewGrid.MinSelectableSize = 12;
+
+WebInspector.OverviewGrid.WindowScrollSpeedFactor = .3;
+
+WebInspector.OverviewGrid.ResizerOffset = 3.5; // half pixel because offset values are not rounded but ceiled
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {Element} parentElement
+ * @param {Element} dividersLabelBarElement
+ */
+WebInspector.OverviewGrid.Window = function(parentElement, dividersLabelBarElement)
+{
+    this._parentElement = parentElement;
+    this._dividersLabelBarElement = dividersLabelBarElement;
+
+    WebInspector.installDragHandle(this._parentElement, this._startWindowSelectorDragging.bind(this), this._windowSelectorDragging.bind(this), this._endWindowSelectorDragging.bind(this), "ew-resize");
+    WebInspector.installDragHandle(this._dividersLabelBarElement, this._startWindowDragging.bind(this), this._windowDragging.bind(this), null, "move");
+
+    this.windowLeft = 0.0;
+    this.windowRight = 1.0;
+
+    this._parentElement.addEventListener("mousewheel", this._onMouseWheel.bind(this), true);
+    this._parentElement.addEventListener("dblclick", this._resizeWindowMaximum.bind(this), true);
+
+    this._overviewWindowElement = document.createElement("div");
+    this._overviewWindowElement.className = "overview-grid-window";
+    parentElement.appendChild(this._overviewWindowElement);
+
+    this._overviewWindowBordersElement = document.createElement("div");
+    this._overviewWindowBordersElement.className = "overview-grid-window-rulers";
+    parentElement.appendChild(this._overviewWindowBordersElement);
+
+    var overviewDividersBackground = document.createElement("div");
+    overviewDividersBackground.className = "overview-grid-dividers-background";
+    parentElement.appendChild(overviewDividersBackground);
+
+    this._leftResizeElement = document.createElement("div");
+    this._leftResizeElement.className = "overview-grid-window-resizer";
+    this._leftResizeElement.style.left = 0;
+    parentElement.appendChild(this._leftResizeElement);
+    WebInspector.installDragHandle(this._leftResizeElement, this._resizerElementStartDragging.bind(this), this._leftResizeElementDragging.bind(this), null, "ew-resize");
+
+    this._rightResizeElement = document.createElement("div");
+    this._rightResizeElement.className = "overview-grid-window-resizer overview-grid-window-resizer-right";
+    this._rightResizeElement.style.right = 0;
+    parentElement.appendChild(this._rightResizeElement);
+    WebInspector.installDragHandle(this._rightResizeElement, this._resizerElementStartDragging.bind(this), this._rightResizeElementDragging.bind(this), null, "ew-resize");
+}
+
+WebInspector.OverviewGrid.Events = {
+    WindowChanged: "WindowChanged"
+}
+
+WebInspector.OverviewGrid.Window.prototype = {
+    reset: function()
+    {
+        this.windowLeft = 0.0;
+        this.windowRight = 1.0;
+
+        this._overviewWindowElement.style.left = "0%";
+        this._overviewWindowElement.style.width = "100%";
+        this._overviewWindowBordersElement.style.left = "0%";
+        this._overviewWindowBordersElement.style.right = "0%";
+        this._leftResizeElement.style.left = "0%";
+        this._rightResizeElement.style.left = "100%";
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _resizerElementStartDragging: function(event)
+    {
+        this._resizerParentOffsetLeft = event.pageX - event.offsetX - event.target.offsetLeft;
+        event.preventDefault();
+        return true;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _leftResizeElementDragging: function(event)
+    {
+        this._resizeWindowLeft(event.pageX - this._resizerParentOffsetLeft);
+        event.preventDefault();
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _rightResizeElementDragging: function(event)
+    {
+        this._resizeWindowRight(event.pageX - this._resizerParentOffsetLeft);
+        event.preventDefault();
+    },
+
+    /**
+     * @param {Event} event
+     * @return {boolean}
+     */
+    _startWindowSelectorDragging: function(event)
+    {
+        this._offsetLeft = event.pageX - event.offsetX;
+        var position = event.pageX - this._offsetLeft;
+        this._overviewWindowSelector = new WebInspector.OverviewGrid.WindowSelector(this._parentElement, position);
+        return true;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _windowSelectorDragging: function(event)
+    {
+        this._overviewWindowSelector._updatePosition(event.pageX - this._offsetLeft);
+        event.preventDefault();
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _endWindowSelectorDragging: function(event)
+    {
+        var window = this._overviewWindowSelector._close(event.pageX - this._offsetLeft);
+        delete this._overviewWindowSelector;
+        if (window.end === window.start) { // Click, not drag.
+            var middle = window.end;
+            window.start = Math.max(0, middle - WebInspector.OverviewGrid.MinSelectableSize / 2);
+            window.end = Math.min(this._parentElement.clientWidth, middle + WebInspector.OverviewGrid.MinSelectableSize / 2);
+        } else if (window.end - window.start < WebInspector.OverviewGrid.MinSelectableSize) {
+            if (this._parentElement.clientWidth - window.end > WebInspector.OverviewGrid.MinSelectableSize)
+                window.end = window.start + WebInspector.OverviewGrid.MinSelectableSize;
+            else
+                window.start = window.end - WebInspector.OverviewGrid.MinSelectableSize;
+        }
+        this._setWindowPosition(window.start, window.end);
+    },
+
+    /**
+     * @param {Event} event
+     * @return {boolean}
+     */
+    _startWindowDragging: function(event)
+    {
+        this._dragStartPoint = event.pageX;
+        this._dragStartLeft = this.windowLeft;
+        this._dragStartRight = this.windowRight;
+        return true;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _windowDragging: function(event)
+    {
+        event.preventDefault();
+        var delta = (event.pageX - this._dragStartPoint) / this._parentElement.clientWidth;
+        if (this._dragStartLeft + delta < 0)
+            delta = -this._dragStartLeft;
+
+        if (this._dragStartRight + delta > 1)
+            delta = 1 - this._dragStartRight;
+
+        this._setWindow(this._dragStartLeft + delta, this._dragStartRight + delta);
+    },
+
+    /**
+     * @param {number} start
+     */
+    _resizeWindowLeft: function(start)
+    {
+        // Glue to edge.
+        if (start < 10)
+            start = 0;
+        else if (start > this._rightResizeElement.offsetLeft -  4)
+            start = this._rightResizeElement.offsetLeft - 4;
+        this._setWindowPosition(start, null);
+    },
+
+    /**
+     * @param {number} end
+     */
+    _resizeWindowRight: function(end)
+    {
+        // Glue to edge.
+        if (end > this._parentElement.clientWidth - 10)
+            end = this._parentElement.clientWidth;
+        else if (end < this._leftResizeElement.offsetLeft + WebInspector.OverviewGrid.MinSelectableSize)
+            end = this._leftResizeElement.offsetLeft + WebInspector.OverviewGrid.MinSelectableSize;
+        this._setWindowPosition(null, end);
+    },
+
+    _resizeWindowMaximum: function()
+    {
+        this._setWindowPosition(0, this._parentElement.clientWidth);
+    },
+
+    /**
+     * @param {number} windowLeft
+     * @param {number} windowRight
+     */
+    _setWindow: function(windowLeft, windowRight)
+    {
+        var clientWidth = this._parentElement.clientWidth;
+        const rulerAdjustment = 1 / clientWidth;
+
+        this.windowLeft = windowLeft;
+        this._leftResizeElement.style.left = this.windowLeft * 100 + "%";
+        this.windowRight = windowRight;
+        this._rightResizeElement.style.left = this.windowRight * 100 + "%";
+
+        this._overviewWindowElement.style.left = this.windowLeft * 100 + "%";
+        this._overviewWindowBordersElement.style.left = (this.windowLeft - rulerAdjustment) * 100 + "%";
+        this._overviewWindowElement.style.width = (this.windowRight - this.windowLeft) * 100 + "%";
+        this._overviewWindowBordersElement.style.right = (1 - this.windowRight + 2 * rulerAdjustment) * 100 + "%";
+
+        this.dispatchEventToListeners(WebInspector.OverviewGrid.Events.WindowChanged);
+    },
+
+    /**
+     * @param {?number} start
+     * @param {?number} end
+     */
+    _setWindowPosition: function(start, end)
+    {
+        var clientWidth = this._parentElement.clientWidth;
+        var windowLeft = typeof start === "number" ? start / clientWidth : this.windowLeft;
+        var windowRight = typeof end === "number" ? end / clientWidth : this.windowRight;
+        this._setWindow(windowLeft, windowRight);
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onMouseWheel: function(event)
+    {
+        if (typeof event.wheelDeltaY === "number" && event.wheelDeltaY) {
+            const zoomFactor = 1.1;
+            const mouseWheelZoomSpeed = 1 / 120;
+
+            var reference = event.offsetX / event.target.clientWidth;
+            this._zoom(Math.pow(zoomFactor, -event.wheelDeltaY * mouseWheelZoomSpeed), reference);
+        }
+        if (typeof event.wheelDeltaX === "number" && event.wheelDeltaX) {
+            var offset = Math.round(event.wheelDeltaX * WebInspector.OverviewGrid.WindowScrollSpeedFactor);
+            var windowLeft = this._leftResizeElement.offsetLeft + WebInspector.OverviewGrid.ResizerOffset;
+            var windowRight = this._rightResizeElement.offsetLeft + WebInspector.OverviewGrid.ResizerOffset;
+
+            if (windowLeft - offset < 0)
+                offset = windowLeft;
+
+            if (windowRight - offset > this._parentElement.clientWidth)
+                offset = windowRight - this._parentElement.clientWidth;
+
+            this._setWindowPosition(windowLeft - offset, windowRight - offset);
+
+            event.preventDefault();
+        }
+    },
+
+    /**
+     * @param {number} factor
+     * @param {number} reference
+     */
+    _zoom: function(factor, reference)
+    {
+        var left = this.windowLeft;
+        var right = this.windowRight;
+        var windowSize = right - left;
+        var newWindowSize = factor * windowSize;
+        var minWindowSize = WebInspector.OverviewGrid.MinSelectableSize / this._parentElement.clientWidth;
+
+        newWindowSize = Number.constrain(newWindowSize, minWindowSize, 1);
+        factor = newWindowSize / windowSize;
+
+        left = reference + (left - reference) * factor;
+        left = Number.constrain(left, 0, 1 - newWindowSize);
+
+        right = reference + (right - reference) * factor;
+        right = Number.constrain(right, newWindowSize, 1);
+        this._setWindow(left, right);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.OverviewGrid.WindowSelector = function(parent, position)
+{
+    this._startPosition = position;
+    this._width = parent.offsetWidth;
+    this._windowSelector = document.createElement("div");
+    this._windowSelector.className = "overview-grid-window-selector";
+    this._windowSelector.style.left = this._startPosition + "px";
+    this._windowSelector.style.right = this._width - this._startPosition + "px";
+    parent.appendChild(this._windowSelector);
+}
+
+WebInspector.OverviewGrid.WindowSelector.prototype = {
+    _createSelectorElement: function(parent, left, width, height)
+    {
+        var selectorElement = document.createElement("div");
+        selectorElement.className = "overview-grid-window-selector";
+        selectorElement.style.left = left + "px";
+        selectorElement.style.width = width + "px";
+        selectorElement.style.top = "0px";
+        selectorElement.style.height = height + "px";
+        parent.appendChild(selectorElement);
+        return selectorElement;
+    },
+
+    _close: function(position)
+    {
+        position = Math.max(0, Math.min(position, this._width));
+        this._windowSelector.parentNode.removeChild(this._windowSelector);
+        return this._startPosition < position ? {start: this._startPosition, end: position} : {start: position, end: this._startPosition};
+    },
+
+    _updatePosition: function(position)
+    {
+        position = Math.max(0, Math.min(position, this._width));
+        if (position < this._startPosition) {
+            this._windowSelector.style.left = position + "px";
+            this._windowSelector.style.right = this._width - this._startPosition + "px";
+        } else {
+            this._windowSelector.style.left = this._startPosition + "px";
+            this._windowSelector.style.right = this._width - position + "px";
+        }
+    }
+}
diff --git a/Source/devtools/front_end/Panel.js b/Source/devtools/front_end/Panel.js
new file mode 100644
index 0000000..b97d1a0
--- /dev/null
+++ b/Source/devtools/front_end/Panel.js
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2007, 2008 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @extends {WebInspector.View}
+ * @constructor
+ */
+WebInspector.Panel = function(name)
+{
+    WebInspector.View.call(this);
+    WebInspector.panels[name] = this;
+
+    this.element.addStyleClass("panel");
+    this.element.addStyleClass(name);
+    this._panelName = name;
+
+    this._shortcuts = /** !Object.<number, function(Event=):boolean> */ ({});
+
+    WebInspector.settings[this._sidebarWidthSettingName()] = WebInspector.settings.createSetting(this._sidebarWidthSettingName(), undefined);
+}
+
+// Should by in sync with style declarations.
+WebInspector.Panel.counterRightMargin = 25;
+
+WebInspector.Panel.prototype = {
+    get name()
+    {
+        return this._panelName;
+    },
+
+    show: function()
+    {
+        WebInspector.View.prototype.show.call(this, WebInspector.inspectorView.panelsElement());
+    },
+
+    wasShown: function()
+    {
+        var panelStatusBar = document.getElementById("panel-status-bar")
+        var drawerViewAnchor = document.getElementById("drawer-view-anchor");
+        var statusBarItems = this.statusBarItems;
+        if (statusBarItems) {
+            this._statusBarItemContainer = document.createElement("div");
+            for (var i = 0; i < statusBarItems.length; ++i)
+                this._statusBarItemContainer.appendChild(statusBarItems[i]);
+            panelStatusBar.insertBefore(this._statusBarItemContainer, drawerViewAnchor);
+        }
+        var statusBarText = this.statusBarText();
+        if (statusBarText) {
+            this._statusBarTextElement = statusBarText;
+            panelStatusBar.appendChild(statusBarText);
+        }
+
+        this.focus();
+    },
+
+    willHide: function()
+    {
+        if (this._statusBarItemContainer && this._statusBarItemContainer.parentNode)
+            this._statusBarItemContainer.parentNode.removeChild(this._statusBarItemContainer);
+        delete this._statusBarItemContainer;
+
+        if (this._statusBarTextElement && this._statusBarTextElement.parentNode)
+            this._statusBarTextElement.parentNode.removeChild(this._statusBarTextElement);
+        delete this._statusBarTextElement;
+    },
+
+    reset: function()
+    {
+        this.searchCanceled();
+    },
+
+    defaultFocusedElement: function()
+    {
+        return this.sidebarTreeElement || this.element;
+    },
+
+    searchCanceled: function()
+    {
+        WebInspector.searchController.updateSearchMatchesCount(0, this);
+    },
+
+    /**
+     * @param {string} query
+     */
+    performSearch: function(query)
+    {
+        // Call searchCanceled since it will reset everything we need before doing a new search.
+        this.searchCanceled();
+    },
+
+    jumpToNextSearchResult: function()
+    {
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canSearchAndReplace: function()
+    {
+        return false;
+    },
+
+    /**
+     * @param {string} text
+     */
+    replaceSelectionWith: function(text)
+    {
+    },
+
+    /**
+     * @param {string} query
+     * @param {string} text
+     */
+    replaceAllWith: function(query, text)
+    {
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canFilter: function()
+    {
+        return false;
+    },
+
+    /**
+     * @param {string} query
+     */
+    performFilter: function(query)
+    {
+    },
+
+    /**
+     * @param {Element=} parentElement
+     * @param {string=} position
+     * @param {number=} defaultWidth
+     * @param {number=} defaultHeight
+     */
+    createSidebarView: function(parentElement, position, defaultWidth, defaultHeight)
+    {
+        if (this.splitView)
+            return;
+
+        if (!parentElement)
+            parentElement = this.element;
+
+        this.splitView = new WebInspector.SidebarView(position, this._sidebarWidthSettingName(), defaultWidth, defaultHeight);
+        this.splitView.show(parentElement);
+        this.splitView.addEventListener(WebInspector.SidebarView.EventTypes.Resized, this.sidebarResized.bind(this));
+
+        this.sidebarElement = this.splitView.sidebarElement;
+    },
+
+    /**
+     * @param {Element=} parentElement
+     * @param {string=} position
+     * @param {number=} defaultWidth
+     */
+    createSidebarViewWithTree: function(parentElement, position, defaultWidth)
+    {
+        if (this.splitView)
+            return;
+
+        this.createSidebarView(parentElement, position);
+
+        this.sidebarTreeElement = document.createElement("ol");
+        this.sidebarTreeElement.className = "sidebar-tree";
+        this.splitView.sidebarElement.appendChild(this.sidebarTreeElement);
+        this.splitView.sidebarElement.addStyleClass("sidebar");
+
+        this.sidebarTree = new TreeOutline(this.sidebarTreeElement);
+        this.sidebarTree.panel = this;
+    },
+
+    _sidebarWidthSettingName: function()
+    {
+        return this._panelName + "SidebarWidth";
+    },
+
+    // Should be implemented by ancestors.
+
+    get statusBarItems()
+    {
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    sidebarResized: function(event)
+    {
+    },
+
+    statusBarResized: function()
+    {
+    },
+
+    /**
+     * @param {Element} anchor
+     * @return {boolean}
+     */
+    canShowAnchorLocation: function(anchor)
+    {
+        return false;
+    },
+
+    /**
+     * @param {Element} anchor
+     */
+    showAnchorLocation: function(anchor)
+    {
+    },
+
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return [];
+    },
+
+    /**
+     * @param {KeyboardEvent} event
+     */
+    handleShortcut: function(event)
+    {
+        var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
+        var handler = this._shortcuts[shortcutKey];
+        if (handler && handler(event))
+            event.handled = true;
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} keys
+     * @param {function(Event=):boolean} handler
+     */
+    registerShortcuts: function(keys, handler)
+    {
+        for (var i = 0; i < keys.length; ++i)
+            this._shortcuts[keys[i].key] = handler;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @param {string} name
+ * @param {string} title
+ * @param {string=} className
+ * @param {string=} scriptName
+ * @param {WebInspector.Panel=} panel
+ */
+WebInspector.PanelDescriptor = function(name, title, className, scriptName, panel)
+{
+    this._name = name;
+    this._title = title;
+    this._className = className;
+    this._scriptName = scriptName;
+    this._panel = panel;
+}
+
+WebInspector.PanelDescriptor.prototype = {
+    /**
+     * @return {string}
+     */
+    name: function()
+    {
+        return this._name;
+    },
+
+    /**
+     * @return {string}
+     */
+    title: function()
+    {
+        return this._title;
+    },
+
+    /**
+     * @return {string}
+     */
+    iconURL: function()
+    {
+        return this._iconURL;
+    },
+
+    /**
+     * @param {string} iconURL
+     */
+    setIconURL: function(iconURL)
+    {
+        this._iconURL = iconURL;
+    },
+
+    /**
+     * @return {WebInspector.Panel}
+     */
+    panel: function()
+    {
+        if (this._panel)
+            return this._panel;
+        if (this._scriptName)
+            loadScript(this._scriptName);
+        this._panel = new WebInspector[this._className];
+        return this._panel;
+    },
+
+    registerShortcuts: function() {}
+}
diff --git a/Source/devtools/front_end/ParsedURL.js b/Source/devtools/front_end/ParsedURL.js
new file mode 100644
index 0000000..a6bca1d
--- /dev/null
+++ b/Source/devtools/front_end/ParsedURL.js
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @param {string} url
+ */
+WebInspector.ParsedURL = function(url)
+{
+    this.isValid = false;
+    this.url = url;
+    this.scheme = "";
+    this.host = "";
+    this.port = "";
+    this.path = "";
+    this.queryParams = "";
+    this.fragment = "";
+    this.folderPathComponents = "";
+    this.lastPathComponent = "";
+
+    // RegExp groups:
+    // 1 - scheme (using the RFC3986 grammar)
+    // 2 - hostname
+    // 3 - ?port
+    // 4 - ?path
+    // 5 - ?fragment
+    var match = url.match(/^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i);
+    if (match) {
+        this.isValid = true;
+        this.scheme = match[1].toLowerCase();
+        this.host = match[2];
+        this.port = match[3];
+        this.path = match[4] || "/";
+        this.fragment = match[5];
+    } else {
+        if (this.url.startsWith("data:")) {
+            this.scheme = "data";
+            return;
+        }
+        if (this.url === "about:blank") {
+            this.scheme = "about";
+            return;
+        }
+        this.path = this.url;
+    }
+
+    // First cut the query params.
+    var path = this.path;
+    var indexOfQuery = path.indexOf("?");
+    if (indexOfQuery !== -1) {
+        this.queryParams = path.substring(indexOfQuery + 1)
+        path = path.substring(0, indexOfQuery);
+    }
+
+    // Then take last path component.
+    var lastSlashIndex = path.lastIndexOf("/");
+    if (lastSlashIndex !== -1) {
+        this.folderPathComponents = path.substring(0, lastSlashIndex);
+        this.lastPathComponent = path.substring(lastSlashIndex + 1);
+    } else
+        this.lastPathComponent = path;
+}
+
+/**
+ * @param {string} url
+ * @return {Array.<string>}
+ */
+WebInspector.ParsedURL.splitURL = function(url)
+{
+    var parsedURL = new WebInspector.ParsedURL(url);
+    var origin;
+    var folderPath;
+    var name;
+    if (parsedURL.isValid) {
+        origin = parsedURL.scheme + "://" + parsedURL.host;
+        if (parsedURL.port)
+            origin += ":" + parsedURL.port;
+        folderPath = parsedURL.folderPathComponents;
+        name = parsedURL.lastPathComponent;
+        if (parsedURL.queryParams)
+            name += "?" + parsedURL.queryParams;
+    } else {
+        origin = "";
+        folderPath = "";
+        name = url;
+    }
+    var result = [origin];
+    var splittedPath = folderPath.split("/");
+    for (var i = 1; i < splittedPath.length; ++i)
+        result.push(splittedPath[i]);
+    result.push(name);
+    return result;
+}
+
+/**
+ * @param {string} baseURL
+ * @param {string} href
+ * @return {?string}
+ */
+WebInspector.ParsedURL.completeURL = function(baseURL, href)
+{
+    if (href) {
+        // Return special URLs as-is.
+        var trimmedHref = href.trim();
+        if (trimmedHref.startsWith("data:") || trimmedHref.startsWith("blob:") || trimmedHref.startsWith("javascript:"))
+            return href;
+
+        // Return absolute URLs as-is.
+        var parsedHref = trimmedHref.asParsedURL();
+        if (parsedHref && parsedHref.scheme)
+            return trimmedHref;
+    } else
+        return baseURL;
+
+    var parsedURL = baseURL.asParsedURL();
+    if (parsedURL) {
+        if (parsedURL.isDataURL())
+            return href;
+        var path = href;
+        if (path.charAt(0) !== "/") {
+            var basePath = parsedURL.path;
+
+            // Trim off the query part of the basePath.
+            var questionMarkIndex = basePath.indexOf("?");
+            if (questionMarkIndex > 0)
+                basePath = basePath.substring(0, questionMarkIndex);
+            // A href of "?foo=bar" implies "basePath?foo=bar".
+            // With "basePath?a=b" and "?foo=bar" we should get "basePath?foo=bar".
+            var prefix;
+            if (path.charAt(0) === "?") {
+                var basePathCutIndex = basePath.indexOf("?");
+                if (basePathCutIndex !== -1)
+                    prefix = basePath.substring(0, basePathCutIndex);
+                else
+                    prefix = basePath;
+            } else
+                prefix = basePath.substring(0, basePath.lastIndexOf("/")) + "/";
+
+            path = prefix + path;
+        } else if (path.length > 1 && path.charAt(1) === "/") {
+            // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol).
+            return parsedURL.scheme + ":" + path;
+        }
+        return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + path;
+    }
+    return null;
+}
+
+WebInspector.ParsedURL.prototype = {
+    get displayName()
+    {
+        if (this._displayName)
+            return this._displayName;
+
+        if (this.isDataURL())
+            return this.dataURLDisplayName();
+        if (this.isAboutBlank())
+            return this.url;
+
+        this._displayName = this.lastPathComponent;
+        if (!this._displayName && this.host)
+            this._displayName = this.host + "/";
+        if (!this._displayName && this.url)
+            this._displayName = this.url.trimURL(WebInspector.inspectedPageDomain ? WebInspector.inspectedPageDomain : "");
+        if (this._displayName === "/")
+            this._displayName = this.url;
+        return this._displayName;
+    },
+
+    dataURLDisplayName: function()
+    {
+        if (this._dataURLDisplayName)
+            return this._dataURLDisplayName;
+        if (!this.isDataURL())
+            return "";
+        this._dataURLDisplayName = this.url.trimEnd(20);
+        return this._dataURLDisplayName;
+    },
+
+    isAboutBlank: function()
+    {
+        return this.url === "about:blank";
+    },
+
+    isDataURL: function()
+    {
+        return this.scheme === "data";
+    }
+}
+
+/**
+ * @return {?WebInspector.ParsedURL}
+ */
+String.prototype.asParsedURL = function()
+{
+    var parsedURL = new WebInspector.ParsedURL(this.toString());
+    if (parsedURL.isValid)
+        return parsedURL;
+    return null;
+}
diff --git a/Source/devtools/front_end/Placard.js b/Source/devtools/front_end/Placard.js
new file mode 100644
index 0000000..4e91a66
--- /dev/null
+++ b/Source/devtools/front_end/Placard.js
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @param {string} title
+ * @param {string} subtitle
+ */
+WebInspector.Placard = function(title, subtitle)
+{
+    this.element = document.createElement("div");
+    this.element.className = "placard";
+    this.element.placard = this;
+
+    this.titleElement = document.createElement("div");
+    this.titleElement.className = "title";
+
+    this.subtitleElement = document.createElement("div");
+    this.subtitleElement.className = "subtitle";
+
+    this.element.appendChild(this.subtitleElement);
+    this.element.appendChild(this.titleElement);
+
+    this.title = title;
+    this.subtitle = subtitle;
+    this.selected = false;
+}
+
+WebInspector.Placard.prototype = {
+    /** @return {string} */
+    get title()
+    {
+        return this._title;
+    },
+
+    set title(x)
+    {
+        if (this._title === x)
+            return;
+        this._title = x;
+        this.titleElement.textContent = x;
+    },
+
+    /** @return {string} */
+    get subtitle()
+    {
+        return this._subtitle;
+    },
+
+    set subtitle(x)
+    {
+        if (this._subtitle === x)
+            return;
+        this._subtitle = x;
+        this.subtitleElement.textContent = x;
+    },
+
+    /** @return {boolean} */
+    get selected()
+    {
+        return this._selected;
+    },
+
+    set selected(x)
+    {
+        if (x)
+            this.select();
+        else
+            this.deselect();
+    },
+
+    select: function()
+    {
+        if (this._selected)
+            return;
+        this._selected = true;
+        this.element.addStyleClass("selected");
+    },
+
+    deselect: function()
+    {
+        if (!this._selected)
+            return;
+        this._selected = false;
+        this.element.removeStyleClass("selected");
+    },
+
+    toggleSelected: function()
+    {
+        this.selected = !this.selected;
+    },
+
+    discard: function()
+    {
+    }
+}
diff --git a/Source/devtools/front_end/Popover.js b/Source/devtools/front_end/Popover.js
new file mode 100644
index 0000000..5dbecb9
--- /dev/null
+++ b/Source/devtools/front_end/Popover.js
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.PopoverHelper=} popoverHelper
+ */
+WebInspector.Popover = function(popoverHelper)
+{
+    WebInspector.View.call(this);
+    this.markAsRoot();
+    this.element.className = "popover custom-popup-vertical-scroll custom-popup-horizontal-scroll";
+
+    this._popupArrowElement = document.createElement("div");
+    this._popupArrowElement.className = "arrow";
+    this.element.appendChild(this._popupArrowElement);
+
+    this._contentDiv = document.createElement("div");
+    this._contentDiv.className = "content";
+    this.element.appendChild(this._contentDiv);
+
+    this._popoverHelper = popoverHelper;
+}
+
+WebInspector.Popover.prototype = {
+    /**
+     * @param {Element} element
+     * @param {Element|AnchorBox} anchor
+     * @param {?number=} preferredWidth
+     * @param {?number=} preferredHeight
+     * @param {?WebInspector.Popover.Orientation=} arrowDirection
+     */
+    show: function(element, anchor, preferredWidth, preferredHeight, arrowDirection)
+    {
+        this._innerShow(null, element, anchor, preferredWidth, preferredHeight, arrowDirection);
+    },
+
+    /**
+     * @param {WebInspector.View} view
+     * @param {Element|AnchorBox} anchor
+     * @param {?number=} preferredWidth
+     * @param {?number=} preferredHeight
+     */
+    showView: function(view, anchor, preferredWidth, preferredHeight)
+    {
+        this._innerShow(view, view.element, anchor, preferredWidth, preferredHeight);
+    },
+
+    /**
+     * @param {WebInspector.View?} view
+     * @param {Element} contentElement
+     * @param {Element|AnchorBox} anchor
+     * @param {?number=} preferredWidth
+     * @param {?number=} preferredHeight
+     * @param {?WebInspector.Popover.Orientation=} arrowDirection
+     */
+    _innerShow: function(view, contentElement, anchor, preferredWidth, preferredHeight, arrowDirection)
+    {
+        if (this._disposed)
+            return;
+        this.contentElement = contentElement;
+
+        // This should not happen, but we hide previous popup to be on the safe side.
+        if (WebInspector.Popover._popover)
+            WebInspector.Popover._popover.detach();
+        WebInspector.Popover._popover = this;
+
+        // Temporarily attach in order to measure preferred dimensions.
+        var preferredSize = view ? view.measurePreferredSize() : this.contentElement.measurePreferredSize();
+        preferredWidth = preferredWidth || preferredSize.width;
+        preferredHeight = preferredHeight || preferredSize.height;
+
+        WebInspector.View.prototype.show.call(this, document.body);
+
+        if (view)
+            view.show(this._contentDiv);
+        else
+            this._contentDiv.appendChild(this.contentElement);
+
+        this._positionElement(anchor, preferredWidth, preferredHeight, arrowDirection);
+
+        if (this._popoverHelper) {
+            contentElement.addEventListener("mousemove", this._popoverHelper._killHidePopoverTimer.bind(this._popoverHelper), true);
+            this.element.addEventListener("mouseout", this._popoverHelper._popoverMouseOut.bind(this._popoverHelper), true);
+        }
+    },
+
+    hide: function()
+    {
+        this.detach();
+        delete WebInspector.Popover._popover;
+    },
+
+    get disposed()
+    {
+        return this._disposed;
+    },
+
+    dispose: function()
+    {
+        if (this.isShowing())
+            this.hide();
+        this._disposed = true;
+    },
+
+    setCanShrink: function(canShrink)
+    {
+        this._hasFixedHeight = !canShrink;
+        this._contentDiv.addStyleClass("fixed-height");
+    },
+
+    /**
+     * @param {Element|AnchorBox} anchorElement
+     * @param {number} preferredWidth
+     * @param {number} preferredHeight
+     * @param {?WebInspector.Popover.Orientation=} arrowDirection
+     */
+    _positionElement: function(anchorElement, preferredWidth, preferredHeight, arrowDirection)
+    {
+        const borderWidth = 25;
+        const scrollerWidth = this._hasFixedHeight ? 0 : 11;
+        const arrowHeight = 15;
+        const arrowOffset = 10;
+        const borderRadius = 10;
+
+        // Skinny tooltips are not pretty, their arrow location is not nice.
+        preferredWidth = Math.max(preferredWidth, 50);
+        const totalWidth = window.innerWidth;
+        const totalHeight = window.innerHeight;
+
+        var anchorBox = anchorElement instanceof AnchorBox ? anchorElement : anchorElement.boxInWindow(window);
+        var newElementPosition = { x: 0, y: 0, width: preferredWidth + scrollerWidth, height: preferredHeight };
+
+        var verticalAlignment;
+        var roomAbove = anchorBox.y;
+        var roomBelow = totalHeight - anchorBox.y - anchorBox.height;
+
+        if ((roomAbove > roomBelow) || (arrowDirection === WebInspector.Popover.Orientation.Bottom)) {
+            // Positioning above the anchor.
+            if ((anchorBox.y > newElementPosition.height + arrowHeight + borderRadius) || (arrowDirection === WebInspector.Popover.Orientation.Bottom))
+                newElementPosition.y = anchorBox.y - newElementPosition.height - arrowHeight;
+            else {
+                newElementPosition.y = borderRadius;
+                newElementPosition.height = anchorBox.y - borderRadius * 2 - arrowHeight;
+                if (this._hasFixedHeight && newElementPosition.height < preferredHeight) {
+                    newElementPosition.y = borderRadius;
+                    newElementPosition.height = preferredHeight;
+                }
+            }
+            verticalAlignment = WebInspector.Popover.Orientation.Bottom;
+        } else {
+            // Positioning below the anchor.
+            newElementPosition.y = anchorBox.y + anchorBox.height + arrowHeight;
+            if ((newElementPosition.y + newElementPosition.height + arrowHeight - borderWidth >= totalHeight) && (arrowDirection !== WebInspector.Popover.Orientation.Top)) {
+                newElementPosition.height = totalHeight - anchorBox.y - anchorBox.height - borderRadius * 2 - arrowHeight;
+                if (this._hasFixedHeight && newElementPosition.height < preferredHeight) {
+                    newElementPosition.y = totalHeight - preferredHeight - borderRadius;
+                    newElementPosition.height = preferredHeight;
+                }
+            }
+            // Align arrow.
+            verticalAlignment = WebInspector.Popover.Orientation.Top;
+        }
+
+        var horizontalAlignment;
+        if (anchorBox.x + newElementPosition.width < totalWidth) {
+            newElementPosition.x = Math.max(borderRadius, anchorBox.x - borderRadius - arrowOffset);
+            horizontalAlignment = "left";
+        } else if (newElementPosition.width + borderRadius * 2 < totalWidth) {
+            newElementPosition.x = totalWidth - newElementPosition.width - borderRadius;
+            horizontalAlignment = "right";
+            // Position arrow accurately.
+            var arrowRightPosition = Math.max(0, totalWidth - anchorBox.x - anchorBox.width - borderRadius - arrowOffset);
+            arrowRightPosition += anchorBox.width / 2;
+            arrowRightPosition = Math.min(arrowRightPosition, newElementPosition.width - borderRadius - arrowOffset);
+            this._popupArrowElement.style.right = arrowRightPosition + "px";
+        } else {
+            newElementPosition.x = borderRadius;
+            newElementPosition.width = totalWidth - borderRadius * 2;
+            newElementPosition.height += scrollerWidth;
+            horizontalAlignment = "left";
+            if (verticalAlignment === WebInspector.Popover.Orientation.Bottom)
+                newElementPosition.y -= scrollerWidth;
+            // Position arrow accurately.
+            this._popupArrowElement.style.left = Math.max(0, anchorBox.x - borderRadius * 2 - arrowOffset) + "px";
+            this._popupArrowElement.style.left += anchorBox.width / 2;
+        }
+
+        this.element.className = "popover custom-popup-vertical-scroll custom-popup-horizontal-scroll " + verticalAlignment + "-" + horizontalAlignment + "-arrow";
+        this.element.positionAt(newElementPosition.x - borderWidth, newElementPosition.y - borderWidth);
+        this.element.style.width = newElementPosition.width + borderWidth * 2 + "px";
+        this.element.style.height = newElementPosition.height + borderWidth * 2 + "px";
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @param {Element} panelElement
+ * @param {function(Element, Event):(Element|AnchorBox)|undefined} getAnchor
+ * @param {function(Element, WebInspector.Popover):undefined} showPopover
+ * @param {function()=} onHide
+ * @param {boolean=} disableOnClick
+ */
+WebInspector.PopoverHelper = function(panelElement, getAnchor, showPopover, onHide, disableOnClick)
+{
+    this._panelElement = panelElement;
+    this._getAnchor = getAnchor;
+    this._showPopover = showPopover;
+    this._onHide = onHide;
+    this._disableOnClick = !!disableOnClick;
+    panelElement.addEventListener("mousedown", this._mouseDown.bind(this), false);
+    panelElement.addEventListener("mousemove", this._mouseMove.bind(this), false);
+    panelElement.addEventListener("mouseout", this._mouseOut.bind(this), false);
+    this.setTimeout(1000);
+}
+
+WebInspector.PopoverHelper.prototype = {
+    setTimeout: function(timeout)
+    {
+        this._timeout = timeout;
+    },
+
+    /**
+     * @param {MouseEvent} event
+     * @return {boolean}
+     */
+    _eventInHoverElement: function(event)
+    {
+        if (!this._hoverElement)
+            return false;
+        var box = this._hoverElement instanceof AnchorBox ? this._hoverElement : this._hoverElement.boxInWindow();
+        return (box.x <= event.clientX && event.clientX <= box.x + box.width &&
+            box.y <= event.clientY && event.clientY <= box.y + box.height);
+    },
+
+    _mouseDown: function(event)
+    {
+        if (this._disableOnClick || !this._eventInHoverElement(event))
+            this.hidePopover();
+        else {
+            this._killHidePopoverTimer();
+            this._handleMouseAction(event, true);
+        }
+    },
+
+    _mouseMove: function(event)
+    {
+        // Pretend that nothing has happened.
+        if (this._eventInHoverElement(event))
+            return;
+
+        this._startHidePopoverTimer();
+        this._handleMouseAction(event, false);
+    },
+
+    _popoverMouseOut: function(event)
+    {
+        if (!this.isPopoverVisible())
+            return;
+        if (event.relatedTarget && !event.relatedTarget.isSelfOrDescendant(this._popover._contentDiv))
+            this._startHidePopoverTimer();
+    },
+
+    _mouseOut: function(event)
+    {
+        if (!this.isPopoverVisible())
+            return;
+        if (!this._eventInHoverElement(event))
+            this._startHidePopoverTimer();
+    },
+
+    _startHidePopoverTimer: function()
+    {
+        // User has 500ms (this._timeout / 2) to reach the popup.
+        if (!this._popover || this._hidePopoverTimer)
+            return;
+
+        function doHide()
+        {
+            this._hidePopover();
+            delete this._hidePopoverTimer;
+        }
+        this._hidePopoverTimer = setTimeout(doHide.bind(this), this._timeout / 2);
+    },
+
+    _handleMouseAction: function(event, isMouseDown)
+    {
+        this._resetHoverTimer();
+        if (event.which && this._disableOnClick)
+            return;
+        this._hoverElement = this._getAnchor(event.target, event);
+        if (!this._hoverElement)
+            return;
+        const toolTipDelay = isMouseDown ? 0 : (this._popup ? this._timeout * 0.6 : this._timeout);
+        this._hoverTimer = setTimeout(this._mouseHover.bind(this, this._hoverElement), toolTipDelay);
+    },
+
+    _resetHoverTimer: function()
+    {
+        if (this._hoverTimer) {
+            clearTimeout(this._hoverTimer);
+            delete this._hoverTimer;
+        }
+    },
+
+    isPopoverVisible: function()
+    {
+        return !!this._popover;
+    },
+
+    hidePopover: function()
+    {
+        this._resetHoverTimer();
+        this._hidePopover();
+    },
+
+    _hidePopover: function()
+    {
+        if (!this._popover)
+            return;
+
+        if (this._onHide)
+            this._onHide();
+
+        this._popover.dispose();
+        delete this._popover;
+        this._hoverElement = null;
+    },
+
+    _mouseHover: function(element)
+    {
+        delete this._hoverTimer;
+
+        this._hidePopover();
+        this._popover = new WebInspector.Popover(this);
+        this._showPopover(element, this._popover);
+    },
+
+    _killHidePopoverTimer: function()
+    {
+        if (this._hidePopoverTimer) {
+            clearTimeout(this._hidePopoverTimer);
+            delete this._hidePopoverTimer;
+
+            // We know that we reached the popup, but we might have moved over other elements.
+            // Discard pending command.
+            this._resetHoverTimer();
+        }
+    }
+}
+
+/** @enum {string} */
+WebInspector.Popover.Orientation = {
+    Top: "top",
+    Bottom: "bottom"
+}
+
+/**
+ * @constructor
+ * @param {string} title
+ */
+WebInspector.PopoverContentHelper = function(title)
+{
+    this._contentTable = document.createElement("table");
+    var titleCell = this._createCell(WebInspector.UIString("%s - Details", title), "popover-details-title");
+    titleCell.colSpan = 2;
+    var titleRow = document.createElement("tr");
+    titleRow.appendChild(titleCell);
+    this._contentTable.appendChild(titleRow);
+}
+
+WebInspector.PopoverContentHelper.prototype = {
+    contentTable: function()
+    {
+        return this._contentTable;
+    },
+
+    /**
+     * @param {string=} styleName
+     */
+    _createCell: function(content, styleName)
+    {
+        var text = document.createElement("label");
+        text.appendChild(document.createTextNode(content));
+        var cell = document.createElement("td");
+        cell.className = "popover-details";
+        if (styleName)
+            cell.className += " " + styleName;
+        cell.textContent = content;
+        return cell;
+    },
+
+    appendTextRow: function(title, content)
+    {
+        var row = document.createElement("tr");
+        row.appendChild(this._createCell(title, "popover-details-row-title"));
+        row.appendChild(this._createCell(content, "popover-details-row-data"));
+        this._contentTable.appendChild(row);
+    },
+
+    /**
+     * @param {string=} titleStyle
+     */
+    appendElementRow: function(title, content, titleStyle)
+    {
+        var row = document.createElement("tr");
+        var titleCell = this._createCell(title, "popover-details-row-title");
+        if (titleStyle)
+            titleCell.addStyleClass(titleStyle);
+        row.appendChild(titleCell);
+        var cell = document.createElement("td");
+        cell.className = "details";
+        cell.appendChild(content);
+        row.appendChild(cell);
+        this._contentTable.appendChild(row);
+    },
+
+    appendStackTrace: function(title, stackTrace, callFrameLinkifier)
+    {
+        this.appendTextRow("", "");
+        var framesTable = document.createElement("table");
+        for (var i = 0; i < stackTrace.length; ++i) {
+            var stackFrame = stackTrace[i];
+            var row = document.createElement("tr");
+            row.className = "details";
+            row.appendChild(this._createCell(stackFrame.functionName ? stackFrame.functionName : WebInspector.UIString("(anonymous function)"), "function-name"));
+            row.appendChild(this._createCell(" @ "));
+            var linkCell = document.createElement("td");
+            var urlElement = callFrameLinkifier(stackFrame);
+            linkCell.appendChild(urlElement);
+            row.appendChild(linkCell);
+            framesTable.appendChild(row);
+        }
+        this.appendElementRow(title, framesTable, "popover-stacktrace-title");
+    }
+}
diff --git a/Source/devtools/front_end/PresentationConsoleMessageHelper.js b/Source/devtools/front_end/PresentationConsoleMessageHelper.js
new file mode 100644
index 0000000..25533ed
--- /dev/null
+++ b/Source/devtools/front_end/PresentationConsoleMessageHelper.js
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.UISourceCodeProvider} uiSourceCodeProvider
+ */
+WebInspector.PresentationConsoleMessageHelper = function(uiSourceCodeProvider)
+{
+    /**
+     * @type {Object.<string, Array.<WebInspector.ConsoleMessage>>}
+     */
+    this._pendingConsoleMessages = {};
+    this._presentationConsoleMessages = [];
+    this._uiSourceCodeProvider = uiSourceCodeProvider;
+
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._consoleMessageAdded, this);
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
+
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._parsedScriptSource, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
+}
+
+WebInspector.PresentationConsoleMessageHelper.prototype = {
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _consoleMessageAdded: function(event)
+    {
+        var message = /** @type {WebInspector.ConsoleMessage} */ (event.data);
+        if (!message.url || !message.isErrorOrWarning())
+            return;
+
+        var rawLocation = message.location();
+        if (rawLocation)
+            this._addConsoleMessageToScript(message, rawLocation);
+        else
+            this._addPendingConsoleMessage(message);
+    },
+
+    /**
+     * @param {WebInspector.ConsoleMessage} message
+     * @param {WebInspector.DebuggerModel.Location} rawLocation
+     */
+    _addConsoleMessageToScript: function(message, rawLocation)
+    {
+        this._presentationConsoleMessages.push(new WebInspector.PresentationConsoleMessage(message, rawLocation));
+    },
+
+    /**
+     * @param {WebInspector.ConsoleMessage} message
+     */
+    _addPendingConsoleMessage: function(message)
+    {
+        if (!message.url)
+            return;
+        if (!this._pendingConsoleMessages[message.url])
+            this._pendingConsoleMessages[message.url] = [];
+        this._pendingConsoleMessages[message.url].push(message);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _parsedScriptSource: function(event)
+    {
+        var script = /** @type {WebInspector.Script} */ (event.data);
+
+        var messages = this._pendingConsoleMessages[script.sourceURL];
+        if (!messages)
+            return;
+
+        var pendingMessages = [];
+        for (var i = 0; i < messages.length; i++) {
+            var message = messages[i];
+            var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (message.location());
+            if (script.scriptId === rawLocation.scriptId)
+                this._addConsoleMessageToScript(message, rawLocation);
+            else
+                pendingMessages.push(message);
+        }
+
+        if (pendingMessages.length)
+            this._pendingConsoleMessages[script.sourceURL] = pendingMessages;
+        else
+            delete this._pendingConsoleMessages[script.sourceURL];
+    },
+
+    _consoleCleared: function()
+    {
+        this._pendingConsoleMessages = {};
+        for (var i = 0; i < this._presentationConsoleMessages.length; ++i)
+            this._presentationConsoleMessages[i].dispose();
+        this._presentationConsoleMessages = [];
+        var uiSourceCodes = this._uiSourceCodeProvider.uiSourceCodes();
+        for (var i = 0; i < uiSourceCodes.length; ++i)
+            uiSourceCodes[i].consoleMessagesCleared();
+    },
+
+    _debuggerReset: function()
+    {
+        this._pendingConsoleMessages = {};
+        this._presentationConsoleMessages = [];
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.ConsoleMessage} message
+ * @param {WebInspector.DebuggerModel.Location} rawLocation
+ */
+WebInspector.PresentationConsoleMessage = function(message, rawLocation)
+{
+    this.originalMessage = message;
+    this._liveLocation = WebInspector.debuggerModel.createLiveLocation(rawLocation, this._updateLocation.bind(this));
+}
+
+WebInspector.PresentationConsoleMessage.prototype = {
+    /**
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    _updateLocation: function(uiLocation)
+    {
+        if (this._uiLocation)
+            this._uiLocation.uiSourceCode.consoleMessageRemoved(this);
+        this._uiLocation = uiLocation;
+        this._uiLocation.uiSourceCode.consoleMessageAdded(this);
+    },
+
+    get lineNumber()
+    {
+        return this._uiLocation.lineNumber;
+    },
+
+    dispose: function()
+    {
+        this._liveLocation.dispose();
+    }
+}
diff --git a/Source/devtools/front_end/ProfileDataGridTree.js b/Source/devtools/front_end/ProfileDataGridTree.js
new file mode 100644
index 0000000..dc52845
--- /dev/null
+++ b/Source/devtools/front_end/ProfileDataGridTree.js
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2009 280 North 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ * @param {!ProfilerAgent.CPUProfileNode} profileNode
+ * @param {!WebInspector.TopDownProfileDataGridTree} owningTree
+ * @param {boolean} hasChildren
+ */
+WebInspector.ProfileDataGridNode = function(profileNode, owningTree, hasChildren)
+{
+    this.profileNode = profileNode;
+
+    WebInspector.DataGridNode.call(this, null, hasChildren);
+
+    this.tree = owningTree;
+
+    this.childrenByCallUID = {};
+    this.lastComparator = null;
+
+    this.callUID = profileNode.callUID;
+    this.selfTime = profileNode.selfTime;
+    this.totalTime = profileNode.totalTime;
+    this.functionName = profileNode.functionName;
+    this.numberOfCalls = profileNode.numberOfCalls;
+    this.url = profileNode.url;
+}
+
+WebInspector.ProfileDataGridNode.prototype = {
+    get data()
+    {
+        function formatMilliseconds(time)
+        {
+            return WebInspector.UIString("%.0f\u2009ms", time);
+        }
+
+        var data = {};
+
+        data["function"] = this.functionName;
+        data["calls"] = this.numberOfCalls;
+
+        if (this.tree.profileView.showSelfTimeAsPercent.get())
+            data["self"] = WebInspector.UIString("%.2f%", this.selfPercent);
+        else
+            data["self"] = formatMilliseconds(this.selfTime);
+
+        if (this.tree.profileView.showTotalTimeAsPercent.get())
+            data["total"] = WebInspector.UIString("%.2f%", this.totalPercent);
+        else
+            data["total"] = formatMilliseconds(this.totalTime);
+
+        if (this.tree.profileView.showAverageTimeAsPercent.get())
+            data["average"] = WebInspector.UIString("%.2f%", this.averagePercent);
+        else
+            data["average"] = formatMilliseconds(this.averageTime);
+
+        return data;
+    },
+
+    /**
+     * @override
+     * @param {string} columnIdentifier
+     * @return {!Element}
+     */
+    createCell: function(columnIdentifier)
+    {
+        var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
+
+        if (columnIdentifier === "self" && this._searchMatchedSelfColumn)
+            cell.addStyleClass("highlight");
+        else if (columnIdentifier === "total" && this._searchMatchedTotalColumn)
+            cell.addStyleClass("highlight");
+        else if (columnIdentifier === "average" && this._searchMatchedAverageColumn)
+            cell.addStyleClass("highlight");
+        else if (columnIdentifier === "calls" && this._searchMatchedCallsColumn)
+            cell.addStyleClass("highlight");
+
+        if (columnIdentifier !== "function")
+            return cell;
+
+        if (this.profileNode._searchMatchedFunctionColumn)
+            cell.addStyleClass("highlight");
+
+        if (this.profileNode.url) {
+            // FIXME(62725): profileNode should reference a debugger location.
+            var lineNumber = this.profileNode.lineNumber ? this.profileNode.lineNumber - 1 : 0;
+            var urlElement = this.tree.profileView._linkifier.linkifyLocation(this.profileNode.url, lineNumber, 0, "profile-node-file");
+            urlElement.style.maxWidth = "75%";
+            cell.insertBefore(urlElement, cell.firstChild);
+        }
+
+        return cell;
+    },
+
+    select: function(supressSelectedEvent)
+    {
+        WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent);
+        this.tree.profileView._dataGridNodeSelected(this);
+    },
+
+    deselect: function(supressDeselectedEvent)
+    {
+        WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent);
+        this.tree.profileView._dataGridNodeDeselected(this);
+    },
+
+    /**
+     * @param {function(Object, Object)} comparator
+     * @param {boolean} force
+     */
+    sort: function(comparator, force)
+    {
+        var gridNodeGroups = [[this]];
+
+        for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.length; ++gridNodeGroupIndex) {
+            var gridNodes = gridNodeGroups[gridNodeGroupIndex];
+            var count = gridNodes.length;
+
+            for (var index = 0; index < count; ++index) {
+                var gridNode = gridNodes[index];
+
+                // If the grid node is collapsed, then don't sort children (save operation for later).
+                // If the grid node has the same sorting as previously, then there is no point in sorting it again.
+                if (!force && (!gridNode.expanded || gridNode.lastComparator === comparator)) {
+                    if (gridNode.children.length)
+                        gridNode.shouldRefreshChildren = true;
+                    continue;
+                }
+
+                gridNode.lastComparator = comparator;
+
+                var children = gridNode.children;
+                var childCount = children.length;
+
+                if (childCount) {
+                    children.sort(comparator);
+
+                    for (var childIndex = 0; childIndex < childCount; ++childIndex)
+                        children[childIndex]._recalculateSiblings(childIndex);
+
+                    gridNodeGroups.push(children);
+                }
+            }
+        }
+    },
+
+    /**
+     * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+     * @param {number} index
+     */
+    insertChild: function(profileDataGridNode, index)
+    {
+        WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGridNode, index);
+
+        this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNode;
+    },
+
+    /**
+     * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+     */
+    removeChild: function(profileDataGridNode)
+    {
+        WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGridNode);
+
+        delete this.childrenByCallUID[profileDataGridNode.callUID];
+    },
+
+    removeChildren: function()
+    {
+        WebInspector.DataGridNode.prototype.removeChildren.call(this);
+
+        this.childrenByCallUID = {};
+    },
+
+    /**
+     * @param {!WebInspector.ProfileDataGridNode} node
+     */
+    findChild: function(node)
+    {
+        if (!node)
+            return null;
+        return this.childrenByCallUID[node.callUID];
+    },
+
+    get averageTime()
+    {
+        return this.selfTime / Math.max(1, this.numberOfCalls);
+    },
+
+    get averagePercent()
+    {
+        return this.averageTime / this.tree.totalTime * 100.0;
+    },
+
+    get selfPercent()
+    {
+        return this.selfTime / this.tree.totalTime * 100.0;
+    },
+
+    get totalPercent()
+    {
+        return this.totalTime / this.tree.totalTime * 100.0;
+    },
+
+    get _parent()
+    {
+        return this.parent !== this.dataGrid ? this.parent : this.tree;
+    },
+
+    populate: function()
+    {
+        if (this._populated)
+            return;
+        this._populated = true;
+
+        this._sharedPopulate();
+
+        if (this._parent) {
+            var currentComparator = this._parent.lastComparator;
+
+            if (currentComparator)
+                this.sort(currentComparator, true);
+        }
+    },
+
+    // When focusing and collapsing we modify lots of nodes in the tree.
+    // This allows us to restore them all to their original state when we revert.
+    _save: function()
+    {
+        if (this._savedChildren)
+            return;
+
+        this._savedSelfTime = this.selfTime;
+        this._savedTotalTime = this.totalTime;
+        this._savedNumberOfCalls = this.numberOfCalls;
+
+        this._savedChildren = this.children.slice();
+    },
+
+    // When focusing and collapsing we modify lots of nodes in the tree.
+    // This allows us to restore them all to their original state when we revert.
+    _restore: function()
+    {
+        if (!this._savedChildren)
+            return;
+
+        this.selfTime = this._savedSelfTime;
+        this.totalTime = this._savedTotalTime;
+        this.numberOfCalls = this._savedNumberOfCalls;
+
+        this.removeChildren();
+
+        var children = this._savedChildren;
+        var count = children.length;
+
+        for (var index = 0; index < count; ++index) {
+            children[index]._restore();
+            this.appendChild(children[index]);
+        }
+    },
+
+    _merge: function(child, shouldAbsorb)
+    {
+        this.selfTime += child.selfTime;
+
+        if (!shouldAbsorb) {
+            this.totalTime += child.totalTime;
+            this.numberOfCalls += child.numberOfCalls;
+        }
+
+        var children = this.children.slice();
+
+        this.removeChildren();
+
+        var count = children.length;
+
+        for (var index = 0; index < count; ++index) {
+            if (!shouldAbsorb || children[index] !== child)
+                this.appendChild(children[index]);
+        }
+
+        children = child.children.slice();
+        count = children.length;
+
+        for (var index = 0; index < count; ++index) {
+            var orphanedChild = children[index],
+                existingChild = this.childrenByCallUID[orphanedChild.callUID];
+
+            if (existingChild)
+                existingChild._merge(orphanedChild, false);
+            else
+                this.appendChild(orphanedChild);
+        }
+    },
+
+    __proto__: WebInspector.DataGridNode.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.CPUProfileView} profileView
+ * @param {ProfilerAgent.CPUProfileNode} rootProfileNode
+ */
+WebInspector.ProfileDataGridTree = function(profileView, rootProfileNode)
+{
+    this.tree = this;
+    this.children = [];
+
+    this.profileView = profileView;
+
+    this.totalTime = rootProfileNode.totalTime;
+    this.lastComparator = null;
+
+    this.childrenByCallUID = {};
+}
+
+WebInspector.ProfileDataGridTree.prototype = {
+    get expanded()
+    {
+        return true;
+    },
+
+    appendChild: function(child)
+    {
+        this.insertChild(child, this.children.length);
+    },
+
+    insertChild: function(child, index)
+    {
+        this.children.splice(index, 0, child);
+        this.childrenByCallUID[child.callUID] = child;
+    },
+
+    removeChildren: function()
+    {
+        this.children = [];
+        this.childrenByCallUID = {};
+    },
+
+    findChild: WebInspector.ProfileDataGridNode.prototype.findChild,
+    sort: WebInspector.ProfileDataGridNode.prototype.sort,
+
+    _save: function()
+    {
+        if (this._savedChildren)
+            return;
+
+        this._savedTotalTime = this.totalTime;
+        this._savedChildren = this.children.slice();
+    },
+
+    restore: function()
+    {
+        if (!this._savedChildren)
+            return;
+
+        this.children = this._savedChildren;
+        this.totalTime = this._savedTotalTime;
+
+        var children = this.children;
+        var count = children.length;
+
+        for (var index = 0; index < count; ++index)
+            children[index]._restore();
+
+        this._savedChildren = null;
+    }
+}
+
+WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}];
+
+/**
+ * @param {string} property
+ * @param {boolean} isAscending
+ * @return {function(Object, Object)}
+ */
+WebInspector.ProfileDataGridTree.propertyComparator = function(property, isAscending)
+{
+    var comparator = WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property];
+
+    if (!comparator) {
+        if (isAscending) {
+            comparator = function(lhs, rhs)
+            {
+                if (lhs[property] < rhs[property])
+                    return -1;
+
+                if (lhs[property] > rhs[property])
+                    return 1;
+
+                return 0;
+            }
+        } else {
+            comparator = function(lhs, rhs)
+            {
+                if (lhs[property] > rhs[property])
+                    return -1;
+
+                if (lhs[property] < rhs[property])
+                    return 1;
+
+                return 0;
+            }
+        }
+
+        WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property] = comparator;
+    }
+
+    return comparator;
+}
diff --git a/Source/devtools/front_end/ProfileLauncherView.js b/Source/devtools/front_end/ProfileLauncherView.js
new file mode 100644
index 0000000..af301ac
--- /dev/null
+++ b/Source/devtools/front_end/ProfileLauncherView.js
@@ -0,0 +1,188 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {!WebInspector.ProfilesPanel} profilesPanel
+ */
+WebInspector.ProfileLauncherView = function(profilesPanel)
+{
+    WebInspector.View.call(this);
+
+    this._panel = profilesPanel;
+
+    this.element.addStyleClass("profile-launcher-view");
+    this.element.addStyleClass("panel-enabler-view");
+
+    this._contentElement = this.element.createChild("div", "profile-launcher-view-content");
+    this._innerContentElement = this._contentElement.createChild("div");
+
+    this._controlButton = this._contentElement.createChild("button", "control-profiling");
+    this._controlButton.addEventListener("click", this._controlButtonClicked.bind(this), false);
+}
+
+WebInspector.ProfileLauncherView.prototype = {
+    /**
+     * @param {WebInspector.ProfileType} profileType
+     */
+    addProfileType: function(profileType)
+    {
+        var descriptionElement = this._innerContentElement.createChild("h1");
+        descriptionElement.textContent = profileType.description;
+        var decorationElement = profileType.decorationElement();
+        if (decorationElement)
+            this._innerContentElement.appendChild(decorationElement);
+        this._isInstantProfile = profileType.isInstantProfile();
+    },
+
+    _controlButtonClicked: function()
+    {
+        this._panel.toggleRecordButton();
+    },
+
+    _updateControls: function()
+    {
+        if (this._isInstantProfile) {
+            this._controlButton.removeStyleClass("running");
+            this._controlButton.textContent = WebInspector.UIString("Take Snapshot");
+        } else if (this._isProfiling) {
+            this._controlButton.addStyleClass("running");
+            this._controlButton.textContent = WebInspector.UIString("Stop");
+        } else {
+            this._controlButton.removeStyleClass("running");
+            this._controlButton.textContent = WebInspector.UIString("Start");
+        }
+    },
+
+    profileStarted: function()
+    {
+        this._isProfiling = true;
+        this._updateControls();
+    },
+
+    profileFinished: function()
+    {
+        this._isProfiling = false;
+        this._updateControls();
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileLauncherView}
+ * @param {!WebInspector.ProfilesPanel} profilesPanel
+ */
+WebInspector.MultiProfileLauncherView = function(profilesPanel)
+{
+    WebInspector.ProfileLauncherView.call(this, profilesPanel);
+
+    var header = this._innerContentElement.createChild("h1");
+    header.textContent = WebInspector.UIString("Select profiling type");
+
+    this._profileTypeSelectorForm = this._innerContentElement.createChild("form");
+
+    this._innerContentElement.createChild("div", "flexible-space");
+}
+
+WebInspector.MultiProfileLauncherView.EventTypes = {
+    ProfileTypeSelected: "profile-type-selected"
+}
+
+WebInspector.MultiProfileLauncherView.prototype = {
+    /**
+     * @override
+     * @param {WebInspector.ProfileType} profileType
+     */
+    addProfileType: function(profileType)
+    {
+        var checked = !this._profileTypeSelectorForm.children.length;
+        var labelElement = this._profileTypeSelectorForm.createChild("label");
+        labelElement.textContent = profileType.name;
+        var optionElement = document.createElement("input");
+        labelElement.insertBefore(optionElement, labelElement.firstChild);
+        optionElement.type = "radio";
+        optionElement.name = "profile-type";
+        optionElement.style.hidden = true;
+        if (checked) {
+            optionElement.checked = checked;
+            this.dispatchEventToListeners(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, profileType);
+        }
+        optionElement.addEventListener("change", this._profileTypeChanged.bind(this, profileType), false);
+        var descriptionElement = labelElement.createChild("p");
+        descriptionElement.textContent = profileType.description;
+        var decorationElement = profileType.decorationElement();
+        if (decorationElement)
+            labelElement.appendChild(decorationElement);
+    },
+
+    _controlButtonClicked: function()
+    {
+        this._panel.toggleRecordButton();
+    },
+
+    _updateControls: function()
+    {
+        WebInspector.ProfileLauncherView.prototype._updateControls.call(this);
+        var items = this._profileTypeSelectorForm.elements;
+        for (var i = 0; i < items.length; ++i) {
+            if (items[i].type === "radio")
+                items[i].disabled = this._isProfiling;
+        }
+    },
+
+    /**
+     * @param {WebInspector.ProfileType} profileType
+     */
+    _profileTypeChanged: function(profileType, event)
+    {
+        this.dispatchEventToListeners(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, profileType);
+        this._isInstantProfile = profileType.isInstantProfile();
+        this._updateControls();
+    },
+
+    profileStarted: function()
+    {
+        this._isProfiling = true;
+        this._updateControls();
+    },
+
+    profileFinished: function()
+    {
+        this._isProfiling = false;
+        this._updateControls();
+    },
+
+    __proto__: WebInspector.ProfileLauncherView.prototype
+}
+
diff --git a/Source/devtools/front_end/ProfilesPanel.js b/Source/devtools/front_end/ProfilesPanel.js
new file mode 100644
index 0000000..4568261
--- /dev/null
+++ b/Source/devtools/front_end/ProfilesPanel.js
@@ -0,0 +1,1416 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {string} id
+ * @param {string} name
+ */
+WebInspector.ProfileType = function(id, name)
+{
+    this._id = id;
+    this._name = name;
+    /** @type {!Array.<!WebInspector.ProfileHeader>} */
+    this._profiles = [];
+    this._profilesIdMap = {};
+    /** @type {WebInspector.SidebarSectionTreeElement} */
+    this.treeElement = null;
+}
+
+WebInspector.ProfileType.Events = {
+    AddProfileHeader: "add-profile-header",
+    RemoveProfileHeader: "remove-profile-header",
+    ProgressUpdated: "progress-updated",
+    ViewUpdated: "view-updated"
+}
+
+WebInspector.ProfileType.prototype = {
+    /**
+     * @return {string|null}
+     */
+    fileExtension: function()
+    {
+        return null;
+    },
+
+    get statusBarItems()
+    {
+        return [];
+    },
+
+    get buttonTooltip()
+    {
+        return "";
+    },
+
+    get id()
+    {
+        return this._id;
+    },
+
+    get treeItemTitle()
+    {
+        return this._name;
+    },
+
+    get name()
+    {
+        return this._name;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    buttonClicked: function()
+    {
+        return false;
+    },
+
+    get description()
+    {
+        return "";
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isInstantProfile: function()
+    {
+        return false;
+    },
+
+    /**
+     * @return {!Array.<!WebInspector.ProfileHeader>}
+     */
+    getProfiles: function()
+    {
+        return this._profiles.filter(function(profile) { return !profile.isTemporary; });
+    },
+
+    /**
+     * @return {Element}
+     */
+    decorationElement: function()
+    {
+        return null;
+    },
+
+    /**
+     * @nosideeffects
+     * @param {number} uid
+     * @return {WebInspector.ProfileHeader}
+     */
+    getProfile: function(uid)
+    {
+        return this._profilesIdMap[this._makeKey(uid)];
+    },
+
+    // Must be implemented by subclasses.
+    /**
+     * @param {string=} title
+     * @return {!WebInspector.ProfileHeader}
+     */
+    createTemporaryProfile: function(title)
+    {
+        throw new Error("Needs implemented.");
+    },
+
+    /**
+     * @param {ProfilerAgent.ProfileHeader} profile
+     * @return {!WebInspector.ProfileHeader}
+     */
+    createProfile: function(profile)
+    {
+        throw new Error("Not supported for " + this._name + " profiles.");
+    },
+
+    /**
+     * @nosideeffects
+     * @param {number} id
+     * @return {string}
+     */
+    _makeKey: function(id)
+    {
+        return id + '/' + escape(this.id);
+    },
+
+    /**
+     * @param {!WebInspector.ProfileHeader} profile
+     */
+    addProfile: function(profile)
+    {
+        this._profiles.push(profile);
+        // FIXME: uid only based key should be enough.
+        this._profilesIdMap[this._makeKey(profile.uid)] = profile;
+        this.dispatchEventToListeners(WebInspector.ProfileType.Events.AddProfileHeader, profile);
+    },
+
+    /**
+     * @param {!WebInspector.ProfileHeader} profile
+     */
+    removeProfile: function(profile)
+    {
+        for (var i = 0; i < this._profiles.length; ++i) {
+            if (this._profiles[i].uid === profile.uid) {
+                this._profiles.splice(i, 1);
+                break;
+            }
+        }
+        delete this._profilesIdMap[this._makeKey(profile.uid)];
+    },
+
+    /**
+     * @nosideeffects
+     * @return {WebInspector.ProfileHeader}
+     */
+    findTemporaryProfile: function()
+    {
+        for (var i = 0; i < this._profiles.length; ++i) {
+            if (this._profiles[i].isTemporary)
+                return this._profiles[i];
+        }
+        return null;
+    },
+
+    _reset: function()
+    {
+        var profiles = this._profiles.slice(0);
+        for (var i = 0; i < profiles.length; ++i) {
+            var profile = profiles[i];
+            var view = profile.existingView();
+            if (view) {
+                view.detach();
+                if ("dispose" in view)
+                    view.dispose();
+            }
+            this.dispatchEventToListeners(WebInspector.ProfileType.Events.RemoveProfileHeader, profile);
+        }
+        this.treeElement.removeChildren();
+        this._profiles = [];
+        this._profilesIdMap = {};
+    },
+
+    /**
+     * @param {function(this:WebInspector.ProfileType, ?string, !Array.<!ProfilerAgent.ProfileHeader>)} populateCallback
+     */
+    _requestProfilesFromBackend: function(populateCallback)
+    {
+    },
+
+    _populateProfiles: function()
+    {
+        /**
+         * @param {?string} error
+         * @param {!Array.<!ProfilerAgent.ProfileHeader>} profileHeaders
+         */
+        function populateCallback(error, profileHeaders) {
+            if (error)
+                return;
+            profileHeaders.sort(function(a, b) { return a.uid - b.uid; });
+            var count = profileHeaders.length;
+            for (var i = 0; i < count; ++i)
+                this.addProfile(this.createProfile(profileHeaders[i]));
+        }
+        this._requestProfilesFromBackend(populateCallback.bind(this));
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {!WebInspector.ProfileType} profileType
+ * @param {string} title
+ * @param {number=} uid
+ */
+WebInspector.ProfileHeader = function(profileType, title, uid)
+{
+    this._profileType = profileType;
+    this.title = title;
+    this.isTemporary = uid === undefined;
+    this.uid = this.isTemporary ? -1 : uid;
+    this._fromFile = false;
+}
+
+WebInspector.ProfileHeader.prototype = {
+    /**
+     * @return {!WebInspector.ProfileType}
+     */
+    profileType: function()
+    {
+        return this._profileType;
+    },
+
+    /**
+     * Must be implemented by subclasses.
+     * @return {WebInspector.ProfileSidebarTreeElement}
+     */
+    createSidebarTreeElement: function()
+    {
+        throw new Error("Needs implemented.");
+    },
+
+    /**
+     * @return {?WebInspector.View}
+     */
+    existingView: function()
+    {
+        return this._view;
+    },
+
+    /**
+     * @param {!WebInspector.ProfilesPanel} panel
+     * @return {!WebInspector.View}
+     */
+    view: function(panel)
+    {
+        if (!this._view)
+            this._view = this.createView(panel);
+        return this._view;
+    },
+
+    /**
+     * @param {!WebInspector.ProfilesPanel} panel
+     * @return {!WebInspector.View}
+     */
+    createView: function(panel)
+    {
+        throw new Error("Not implemented.");
+    },
+
+    dispose: function()
+    {
+    },
+
+    /**
+     * @param {Function} callback
+     */
+    load: function(callback)
+    {
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canSaveToFile: function()
+    {
+        return false;
+    },
+
+    saveToFile: function()
+    {
+        throw new Error("Needs implemented");
+    },
+
+    /**
+     * @param {File} file
+     */
+    loadFromFile: function(file)
+    {
+        throw new Error("Needs implemented");
+    },
+
+    /**
+     * @return {boolean}
+     */
+    fromFile: function()
+    {
+        return this._fromFile;
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Panel}
+ * @implements {WebInspector.ContextMenu.Provider}
+ * @param {string=} name
+ * @param {WebInspector.ProfileType=} type
+ */
+WebInspector.ProfilesPanel = function(name, type)
+{
+    // If the name is not specified the ProfilesPanel works in multi-profile mode.
+    var singleProfileMode = typeof name !== "undefined";
+    name = name || "profiles";
+    WebInspector.Panel.call(this, name);
+    this.registerRequiredCSS("panelEnablerView.css");
+    this.registerRequiredCSS("heapProfiler.css");
+    this.registerRequiredCSS("profilesPanel.css");
+
+    this.createSidebarViewWithTree();
+
+    this.profilesItemTreeElement = new WebInspector.ProfilesSidebarTreeElement(this);
+    this.sidebarTree.appendChild(this.profilesItemTreeElement);
+
+    this._singleProfileMode = singleProfileMode;
+    this._profileTypesByIdMap = {};
+
+    this.profileViews = document.createElement("div");
+    this.profileViews.id = "profile-views";
+    this.splitView.mainElement.appendChild(this.profileViews);
+
+    this._statusBarButtons = [];
+
+    this.recordButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item");
+    this.recordButton.addEventListener("click", this.toggleRecordButton, this);
+    this._statusBarButtons.push(this.recordButton);
+
+    this.clearResultsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear all profiles."), "clear-status-bar-item");
+    this.clearResultsButton.addEventListener("click", this._clearProfiles, this);
+    this._statusBarButtons.push(this.clearResultsButton);
+
+    this._profileTypeStatusBarItemsContainer = document.createElement("div");
+    this._profileTypeStatusBarItemsContainer.className = "status-bar-items";
+
+    this._profileViewStatusBarItemsContainer = document.createElement("div");
+    this._profileViewStatusBarItemsContainer.className = "status-bar-items";
+
+    if (singleProfileMode) {
+        this._launcherView = this._createLauncherView();
+        this._registerProfileType(/** @type {!WebInspector.ProfileType} */ (type));
+        this._selectedProfileType = type;
+        this._updateProfileTypeSpecificUI();
+    } else {
+        this._launcherView = new WebInspector.MultiProfileLauncherView(this);
+        this._launcherView.addEventListener(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, this._onProfileTypeSelected, this);
+
+        this._registerProfileType(new WebInspector.CPUProfileType());
+        if (!WebInspector.WorkerManager.isWorkerFrontend())
+            this._registerProfileType(new WebInspector.CSSSelectorProfileType());
+        var heapSnapshotProfileType = new WebInspector.HeapSnapshotProfileType();
+        this._registerProfileType(heapSnapshotProfileType);
+        if (WebInspector.experimentsSettings.heapObjectsTracking.isEnabled())
+            this._registerProfileType(new WebInspector.TrackingHeapSnapshotProfileType(this, heapSnapshotProfileType));
+        if (!WebInspector.WorkerManager.isWorkerFrontend() && WebInspector.experimentsSettings.nativeMemorySnapshots.isEnabled()) {
+            this._registerProfileType(new WebInspector.NativeSnapshotProfileType());
+            this._registerProfileType(new WebInspector.NativeMemoryProfileType());
+        }
+        if (!WebInspector.WorkerManager.isWorkerFrontend() && WebInspector.experimentsSettings.canvasInspection.isEnabled())
+            this._registerProfileType(new WebInspector.CanvasProfileType());
+    }
+
+    this._reset();
+
+    this._createFileSelectorElement();
+    this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+    this._registerShortcuts();
+
+    WebInspector.ContextMenu.registerProvider(this);
+}
+
+WebInspector.ProfilesPanel.prototype = {
+    _createFileSelectorElement: function()
+    {
+        if (this._fileSelectorElement)
+            this.element.removeChild(this._fileSelectorElement);
+        this._fileSelectorElement = WebInspector.createFileSelectorElement(this._loadFromFile.bind(this));
+        this.element.appendChild(this._fileSelectorElement);
+    },
+
+    /**
+     * @return {!WebInspector.ProfileLauncherView}
+     */
+    _createLauncherView: function()
+    {
+        return new WebInspector.ProfileLauncherView(this);
+    },
+
+    _findProfileTypeByExtension: function(fileName)
+    {
+        for (var id in this._profileTypesByIdMap) {
+            var type = this._profileTypesByIdMap[id];
+            var extension = type.fileExtension();
+            if (!extension)
+                continue;
+            if (fileName.endsWith(type.fileExtension()))
+                return type;
+        }
+        return null;
+    },
+
+    _registerShortcuts: function()
+    {
+        this.registerShortcuts(WebInspector.ProfilesPanelDescriptor.ShortcutKeys.StartStopRecording, this.toggleRecordButton.bind(this));
+    },
+
+    /**
+     * @param {!File} file
+     */
+    _loadFromFile: function(file)
+    {
+        this._createFileSelectorElement();
+
+        var profileType = this._findProfileTypeByExtension(file.name);
+        if (!profileType) {
+            var extensions = [];
+            for (var id in this._profileTypesByIdMap) {
+                var extension = this._profileTypesByIdMap[id].fileExtension();
+                if (!extension)
+                    continue;
+                extensions.push(extension);
+            }
+            WebInspector.log(WebInspector.UIString("Can't load file. Only files with extensions '%s' can be loaded.", extensions.join("', '")));
+            return;
+        }
+
+        if (!!profileType.findTemporaryProfile()) {
+            WebInspector.log(WebInspector.UIString("Can't load profile when other profile is recording."));
+            return;
+        }
+
+        var temporaryProfile = profileType.createTemporaryProfile(WebInspector.ProfilesPanelDescriptor.UserInitiatedProfileName + "." + file.name);
+        profileType.addProfile(temporaryProfile);
+        temporaryProfile._fromFile = true;
+        temporaryProfile.loadFromFile(file);
+    },
+
+    get statusBarItems()
+    {
+        return this._statusBarButtons.select("element").concat(this._profileTypeStatusBarItemsContainer, this._profileViewStatusBarItemsContainer);
+    },
+
+    /**
+     * @param {WebInspector.Event|Event=} event
+     * @return {boolean}
+     */
+    toggleRecordButton: function(event)
+    {
+        var isProfiling = this._selectedProfileType.buttonClicked();
+        this.setRecordingProfile(this._selectedProfileType.id, isProfiling);
+        return true;
+    },
+
+    _populateAllProfiles: function()
+    {
+        if (this._profilesWereRequested)
+            return;
+        this._profilesWereRequested = true;
+        for (var typeId in this._profileTypesByIdMap)
+            this._profileTypesByIdMap[typeId]._populateProfiles();
+    },
+
+    wasShown: function()
+    {
+        WebInspector.Panel.prototype.wasShown.call(this);
+        this._populateAllProfiles();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onProfileTypeSelected: function(event)
+    {
+        this._selectedProfileType = /** @type {!WebInspector.ProfileType} */ (event.data);
+        this._updateProfileTypeSpecificUI();
+    },
+
+    _updateProfileTypeSpecificUI: function()
+    {
+        this.recordButton.title = this._selectedProfileType.buttonTooltip;
+
+        this._profileTypeStatusBarItemsContainer.removeChildren();
+        var statusBarItems = this._selectedProfileType.statusBarItems;
+        if (statusBarItems) {
+            for (var i = 0; i < statusBarItems.length; ++i)
+                this._profileTypeStatusBarItemsContainer.appendChild(statusBarItems[i]);
+        }
+        this._resize(this.splitView.sidebarWidth());
+    },
+
+    _reset: function()
+    {
+        WebInspector.Panel.prototype.reset.call(this);
+
+        for (var typeId in this._profileTypesByIdMap)
+            this._profileTypesByIdMap[typeId]._reset();
+
+        delete this.visibleView;
+        delete this.currentQuery;
+        this.searchCanceled();
+
+        this._profileGroups = {};
+        this._profilesWereRequested = false;
+        this.recordButton.toggled = false;
+        if (this._selectedProfileType)
+            this.recordButton.title = this._selectedProfileType.buttonTooltip;
+        this._launcherView.profileFinished();
+
+        this.sidebarTreeElement.removeStyleClass("some-expandable");
+
+        this.profileViews.removeChildren();
+        this._profileViewStatusBarItemsContainer.removeChildren();
+
+        this.removeAllListeners();
+
+        this.recordButton.visible = true;
+        this._profileViewStatusBarItemsContainer.removeStyleClass("hidden");
+        this.clearResultsButton.element.removeStyleClass("hidden");
+        this.profilesItemTreeElement.select();
+        this._showLauncherView();
+    },
+
+    _showLauncherView: function()
+    {
+        this.closeVisibleView();
+        this._profileViewStatusBarItemsContainer.removeChildren();
+        this._launcherView.show(this.splitView.mainElement);
+        this.visibleView = this._launcherView;
+    },
+
+    _clearProfiles: function()
+    {
+        ProfilerAgent.clearProfiles();
+        HeapProfilerAgent.clearProfiles();
+        this._reset();
+    },
+
+    _garbageCollectButtonClicked: function()
+    {
+        HeapProfilerAgent.collectGarbage();
+    },
+
+    /**
+     * @param {!WebInspector.ProfileType} profileType
+     */
+    _registerProfileType: function(profileType)
+    {
+        this._profileTypesByIdMap[profileType.id] = profileType;
+        this._launcherView.addProfileType(profileType);
+        profileType.treeElement = new WebInspector.SidebarSectionTreeElement(profileType.treeItemTitle, null, true);
+        profileType.treeElement.hidden = !this._singleProfileMode;
+        this.sidebarTree.appendChild(profileType.treeElement);
+        profileType.treeElement.childrenListElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+        function onAddProfileHeader(event)
+        {
+            this._addProfileHeader(event.data);
+        }
+        function onRemoveProfileHeader(event)
+        {
+            this._removeProfileHeader(event.data);
+        }
+        function onProgressUpdated(event)
+        {
+            this._reportProfileProgress(event.data.profile, event.data.done, event.data.total);
+        }
+        profileType.addEventListener(WebInspector.ProfileType.Events.ViewUpdated, this._updateProfileTypeSpecificUI, this);
+        profileType.addEventListener(WebInspector.ProfileType.Events.AddProfileHeader, onAddProfileHeader, this);
+        profileType.addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, onRemoveProfileHeader, this);
+        profileType.addEventListener(WebInspector.ProfileType.Events.ProgressUpdated, onProgressUpdated, this);
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _handleContextMenuEvent: function(event)
+    {
+        var element = event.srcElement;
+        while (element && !element.treeElement && element !== this.element)
+            element = element.parentElement;
+        if (!element)
+            return;
+        if (element.treeElement && element.treeElement.handleContextMenuEvent) {
+            element.treeElement.handleContextMenuEvent(event, this);
+            return;
+        }
+        if (element !== this.element || event.srcElement === this.sidebarElement) {
+            var contextMenu = new WebInspector.ContextMenu(event);
+            if (this.visibleView instanceof WebInspector.HeapSnapshotView)
+                this.visibleView.populateContextMenu(contextMenu, event);
+            contextMenu.appendItem(WebInspector.UIString("Load\u2026"), this._fileSelectorElement.click.bind(this._fileSelectorElement));
+            contextMenu.show();
+        }
+
+    },
+
+    /**
+     * @nosideeffects
+     * @param {string} text
+     * @param {string} profileTypeId
+     * @return {string}
+     */
+    _makeTitleKey: function(text, profileTypeId)
+    {
+        return escape(text) + '/' + escape(profileTypeId);
+    },
+
+    /**
+     * @param {!WebInspector.ProfileHeader} profile
+     */
+    _addProfileHeader: function(profile)
+    {
+        if (!profile.isTemporary)
+            this._removeTemporaryProfile(profile.profileType().id);
+
+        var profileType = profile.profileType();
+        var typeId = profileType.id;
+        var sidebarParent = profileType.treeElement;
+        sidebarParent.hidden = false;
+        var small = false;
+        var alternateTitle;
+
+        if (!WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(profile.title) && !profile.isTemporary) {
+            var profileTitleKey = this._makeTitleKey(profile.title, typeId);
+            if (!(profileTitleKey in this._profileGroups))
+                this._profileGroups[profileTitleKey] = [];
+
+            var group = this._profileGroups[profileTitleKey];
+            group.push(profile);
+            if (group.length === 2) {
+                // Make a group TreeElement now that there are 2 profiles.
+                group._profilesTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(this, profile.title);
+
+                // Insert at the same index for the first profile of the group.
+                var index = sidebarParent.children.indexOf(group[0]._profilesTreeElement);
+                sidebarParent.insertChild(group._profilesTreeElement, index);
+
+                // Move the first profile to the group.
+                var selected = group[0]._profilesTreeElement.selected;
+                sidebarParent.removeChild(group[0]._profilesTreeElement);
+                group._profilesTreeElement.appendChild(group[0]._profilesTreeElement);
+                if (selected)
+                    group[0]._profilesTreeElement.revealAndSelect();
+
+                group[0]._profilesTreeElement.small = true;
+                group[0]._profilesTreeElement.mainTitle = WebInspector.UIString("Run %d", 1);
+
+                this.sidebarTreeElement.addStyleClass("some-expandable");
+            }
+
+            if (group.length >= 2) {
+                sidebarParent = group._profilesTreeElement;
+                alternateTitle = WebInspector.UIString("Run %d", group.length);
+                small = true;
+            }
+        }
+
+        var profileTreeElement = profile.createSidebarTreeElement();
+        profile.sidebarElement = profileTreeElement;
+        profileTreeElement.small = small;
+        if (alternateTitle)
+            profileTreeElement.mainTitle = alternateTitle;
+        profile._profilesTreeElement = profileTreeElement;
+
+        sidebarParent.appendChild(profileTreeElement);
+        if (!profile.isTemporary) {
+            if (!this.visibleView)
+                this._showProfile(profile);
+            this.dispatchEventToListeners("profile added", {
+                type: typeId
+            });
+        }
+    },
+
+    /**
+     * @param {!WebInspector.ProfileHeader} profile
+     */
+    _removeProfileHeader: function(profile)
+    {
+        profile.dispose();
+        profile.profileType().removeProfile(profile);
+
+        var sidebarParent = profile.profileType().treeElement;
+        var profileTitleKey = this._makeTitleKey(profile.title, profile.profileType().id);
+        var group = this._profileGroups[profileTitleKey];
+        if (group) {
+            group.splice(group.indexOf(profile), 1);
+            if (group.length === 1) {
+                // Move the last profile out of its group and remove the group.
+                var index = sidebarParent.children.indexOf(group._profilesTreeElement);
+                sidebarParent.insertChild(group[0]._profilesTreeElement, index);
+                group[0]._profilesTreeElement.small = false;
+                group[0]._profilesTreeElement.mainTitle = group[0].title;
+                sidebarParent.removeChild(group._profilesTreeElement);
+            }
+            if (group.length !== 0)
+                sidebarParent = group._profilesTreeElement;
+            else
+                delete this._profileGroups[profileTitleKey];
+        }
+        sidebarParent.removeChild(profile._profilesTreeElement);
+
+        // No other item will be selected if there aren't any other profiles, so
+        // make sure that view gets cleared when the last profile is removed.
+        if (!sidebarParent.children.length) {
+            this.profilesItemTreeElement.select();
+            this._showLauncherView();
+            sidebarParent.hidden = !this._singleProfileMode;
+        }
+    },
+
+    /**
+     * @param {!WebInspector.ProfileHeader} profile
+     * @return {WebInspector.View}
+     */
+    _showProfile: function(profile)
+    {
+        if (!profile || profile.isTemporary)
+            return null;
+
+        var view = profile.view(this);
+        if (view === this.visibleView)
+            return view;
+
+        this.closeVisibleView();
+
+        view.show(this.profileViews);
+
+        profile._profilesTreeElement._suppressOnSelect = true;
+        profile._profilesTreeElement.revealAndSelect();
+        delete profile._profilesTreeElement._suppressOnSelect;
+
+        this.visibleView = view;
+
+        this._profileViewStatusBarItemsContainer.removeChildren();
+
+        var statusBarItems = view.statusBarItems;
+        if (statusBarItems)
+            for (var i = 0; i < statusBarItems.length; ++i)
+                this._profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
+
+        return view;
+    },
+
+    /**
+     * @param {HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
+     * @param {string} viewName
+     */
+    showObject: function(snapshotObjectId, viewName)
+    {
+        var heapProfiles = this.getProfileType(WebInspector.HeapSnapshotProfileType.TypeId).getProfiles();
+        for (var i = 0; i < heapProfiles.length; i++) {
+            var profile = heapProfiles[i];
+            // FIXME: allow to choose snapshot if there are several options.
+            if (profile.maxJSObjectId >= snapshotObjectId) {
+                this._showProfile(profile);
+                var view = profile.view(this);
+                view.changeView(viewName, function() {
+                    view.dataGrid.highlightObjectByHeapSnapshotId(snapshotObjectId);
+                });
+                break;
+            }
+        }
+    },
+
+    /**
+     * @param {string} typeId
+     */
+    _createTemporaryProfile: function(typeId)
+    {
+        var type = this.getProfileType(typeId);
+        if (!type.findTemporaryProfile())
+            type.addProfile(type.createTemporaryProfile());
+    },
+
+    /**
+     * @param {string} typeId
+     */
+    _removeTemporaryProfile: function(typeId)
+    {
+        var temporaryProfile = this.getProfileType(typeId).findTemporaryProfile();
+        if (!!temporaryProfile)
+            this._removeProfileHeader(temporaryProfile);
+    },
+
+    /**
+     * @param {string} typeId
+     * @param {number} uid
+     */
+    getProfile: function(typeId, uid)
+    {
+        return this.getProfileType(typeId).getProfile(uid);
+    },
+
+    /**
+     * @param {WebInspector.View} view
+     */
+    showView: function(view)
+    {
+        this._showProfile(view.profile);
+    },
+
+    /**
+     * @param {string} typeId
+     */
+    getProfileType: function(typeId)
+    {
+        return this._profileTypesByIdMap[typeId];
+    },
+
+    /**
+     * @param {string} typeId
+     * @param {string} uid
+     * @return {WebInspector.View}
+     */
+    showProfile: function(typeId, uid)
+    {
+        return this._showProfile(this.getProfile(typeId, Number(uid)));
+    },
+
+    closeVisibleView: function()
+    {
+        if (this.visibleView)
+            this.visibleView.detach();
+        delete this.visibleView;
+    },
+
+    /**
+     * @param {string} query
+     */
+    performSearch: function(query)
+    {
+        this.searchCanceled();
+
+        var searchableViews = this._searchableViews();
+        if (!searchableViews || !searchableViews.length)
+            return;
+
+        var visibleView = this.visibleView;
+
+        var matchesCountUpdateTimeout = null;
+
+        function updateMatchesCount()
+        {
+            WebInspector.searchController.updateSearchMatchesCount(this._totalSearchMatches, this);
+            WebInspector.searchController.updateCurrentMatchIndex(this._currentSearchResultIndex, this);
+            matchesCountUpdateTimeout = null;
+        }
+
+        function updateMatchesCountSoon()
+        {
+            if (matchesCountUpdateTimeout)
+                return;
+            // Update the matches count every half-second so it doesn't feel twitchy.
+            matchesCountUpdateTimeout = setTimeout(updateMatchesCount.bind(this), 500);
+        }
+
+        function finishedCallback(view, searchMatches)
+        {
+            if (!searchMatches)
+                return;
+
+            this._totalSearchMatches += searchMatches;
+            this._searchResults.push(view);
+
+            if (this.searchMatchFound)
+                this.searchMatchFound(view, searchMatches);
+
+            updateMatchesCountSoon.call(this);
+
+            if (view === visibleView)
+                view.jumpToFirstSearchResult();
+        }
+
+        var i = 0;
+        var panel = this;
+        var boundFinishedCallback = finishedCallback.bind(this);
+        var chunkIntervalIdentifier = null;
+
+        // Split up the work into chunks so we don't block the
+        // UI thread while processing.
+
+        function processChunk()
+        {
+            var view = searchableViews[i];
+
+            if (++i >= searchableViews.length) {
+                if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier)
+                    delete panel._currentSearchChunkIntervalIdentifier;
+                clearInterval(chunkIntervalIdentifier);
+            }
+
+            if (!view)
+                return;
+
+            view.currentQuery = query;
+            view.performSearch(query, boundFinishedCallback);
+        }
+
+        processChunk();
+
+        chunkIntervalIdentifier = setInterval(processChunk, 25);
+        this._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier;
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        if (!this.showView || !this._searchResults || !this._searchResults.length)
+            return;
+
+        var showFirstResult = false;
+
+        this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
+        if (this._currentSearchResultIndex === -1) {
+            this._currentSearchResultIndex = 0;
+            showFirstResult = true;
+        }
+
+        var currentView = this._searchResults[this._currentSearchResultIndex];
+
+        if (currentView.showingLastSearchResult()) {
+            if (++this._currentSearchResultIndex >= this._searchResults.length)
+                this._currentSearchResultIndex = 0;
+            currentView = this._searchResults[this._currentSearchResultIndex];
+            showFirstResult = true;
+        }
+
+        WebInspector.searchController.updateCurrentMatchIndex(this._currentSearchResultIndex, this);
+
+        if (currentView !== this.visibleView) {
+            this.showView(currentView);
+            WebInspector.searchController.showSearchField();
+        }
+
+        if (showFirstResult)
+            currentView.jumpToFirstSearchResult();
+        else
+            currentView.jumpToNextSearchResult();
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        if (!this.showView || !this._searchResults || !this._searchResults.length)
+            return;
+
+        var showLastResult = false;
+
+        this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);
+        if (this._currentSearchResultIndex === -1) {
+            this._currentSearchResultIndex = 0;
+            showLastResult = true;
+        }
+
+        var currentView = this._searchResults[this._currentSearchResultIndex];
+
+        if (currentView.showingFirstSearchResult()) {
+            if (--this._currentSearchResultIndex < 0)
+                this._currentSearchResultIndex = (this._searchResults.length - 1);
+            currentView = this._searchResults[this._currentSearchResultIndex];
+            showLastResult = true;
+        }
+
+        WebInspector.searchController.updateCurrentMatchIndex(this._currentSearchResultIndex, this);
+
+        if (currentView !== this.visibleView) {
+            this.showView(currentView);
+            WebInspector.searchController.showSearchField();
+        }
+
+        if (showLastResult)
+            currentView.jumpToLastSearchResult();
+        else
+            currentView.jumpToPreviousSearchResult();
+    },
+
+    /**
+     * @return {!Array.<!WebInspector.ProfileHeader>}
+     */
+    _getAllProfiles: function()
+    {
+        var profiles = [];
+        for (var typeId in this._profileTypesByIdMap)
+            profiles = profiles.concat(this._profileTypesByIdMap[typeId].getProfiles());
+        return profiles;
+    },
+
+    /**
+     * @return {!Array.<!WebInspector.View>}
+     */
+    _searchableViews: function()
+    {
+        var profiles = this._getAllProfiles();
+        var searchableViews = [];
+        for (var i = 0; i < profiles.length; ++i) {
+            var view = profiles[i].view(this);
+            if (view.performSearch)
+                searchableViews.push(view)
+        }
+        var index = searchableViews.indexOf(this.visibleView);
+        if (index > 0) {
+            // Move visibleView to the first position.
+            searchableViews[index] = searchableViews[0];
+            searchableViews[0] = this.visibleView;
+        }
+        return searchableViews;
+    },
+
+    searchMatchFound: function(view, matches)
+    {
+        view.profileHeader._profilesTreeElement.searchMatches = matches;
+    },
+
+    searchCanceled: function()
+    {
+        if (this._searchResults) {
+            for (var i = 0; i < this._searchResults.length; ++i) {
+                var view = this._searchResults[i];
+                if (view.searchCanceled)
+                    view.searchCanceled();
+                delete view.currentQuery;
+            }
+        }
+
+        WebInspector.Panel.prototype.searchCanceled.call(this);
+
+        if (this._currentSearchChunkIntervalIdentifier) {
+            clearInterval(this._currentSearchChunkIntervalIdentifier);
+            delete this._currentSearchChunkIntervalIdentifier;
+        }
+
+        this._totalSearchMatches = 0;
+        this._currentSearchResultIndex = 0;
+        this._searchResults = [];
+
+        var profiles = this._getAllProfiles();
+        for (var i = 0; i < profiles.length; ++i)
+            profiles[i]._profilesTreeElement.searchMatches = 0;
+    },
+
+    /**
+     * @param {!WebInspector.Event} event
+     */
+    sidebarResized: function(event)
+    {
+        var sidebarWidth = /** @type {number} */ (event.data);
+        this._resize(sidebarWidth);
+    },
+
+    onResize: function()
+    {
+        this._resize(this.splitView.sidebarWidth());
+    },
+
+    /**
+     * @param {number} sidebarWidth
+     */
+    _resize: function(sidebarWidth)
+    {
+        var lastItemElement = this._statusBarButtons[this._statusBarButtons.length - 1].element;
+        var left = lastItemElement.totalOffsetLeft() + lastItemElement.offsetWidth;
+        this._profileTypeStatusBarItemsContainer.style.left = left + "px";
+        left += this._profileTypeStatusBarItemsContainer.offsetWidth - 1;
+        this._profileViewStatusBarItemsContainer.style.left = Math.max(left, sidebarWidth) + "px";
+    },
+
+    /**
+     * @param {string} profileType
+     * @param {boolean} isProfiling
+     */
+    setRecordingProfile: function(profileType, isProfiling)
+    {
+        var profileTypeObject = this.getProfileType(profileType);
+        this.recordButton.toggled = isProfiling;
+        this.recordButton.title = profileTypeObject.buttonTooltip;
+        if (isProfiling) {
+            this._launcherView.profileStarted();
+            this._createTemporaryProfile(profileType);
+        } else
+            this._launcherView.profileFinished();
+    },
+
+    /**
+     * @param {!WebInspector.ProfileHeader} profile
+     * @param {number} done
+     * @param {number} total
+     */
+    _reportProfileProgress: function(profile, done, total)
+    {
+        profile.sidebarElement.subtitle = WebInspector.UIString("%.0f%", (done / total) * 100);
+        profile.sidebarElement.wait = true;
+    },
+
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    appendApplicableItems: function(event, contextMenu, target)
+    {
+        if (WebInspector.inspectorView.currentPanel() !== this)
+            return;
+
+        var object = /** @type {WebInspector.RemoteObject} */ (target);
+        var objectId = object.objectId;
+        if (!objectId)
+            return;
+
+        var heapProfiles = this.getProfileType(WebInspector.HeapSnapshotProfileType.TypeId).getProfiles();
+        if (!heapProfiles.length)
+            return;
+
+        function revealInView(viewName)
+        {
+            HeapProfilerAgent.getHeapObjectId(objectId, didReceiveHeapObjectId.bind(this, viewName));
+        }
+
+        function didReceiveHeapObjectId(viewName, error, result)
+        {
+            if (WebInspector.inspectorView.currentPanel() !== this)
+                return;
+            if (!error)
+                this.showObject(result, viewName);
+        }
+
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInView.bind(this, "Dominators"));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInView.bind(this, "Summary"));
+    },
+
+    __proto__: WebInspector.Panel.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarTreeElement}
+ * @param {!WebInspector.ProfileHeader} profile
+ * @param {string} titleFormat
+ * @param {string} className
+ */
+WebInspector.ProfileSidebarTreeElement = function(profile, titleFormat, className)
+{
+    this.profile = profile;
+    this._titleFormat = titleFormat;
+
+    if (WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(this.profile.title))
+        this._profileNumber = WebInspector.ProfilesPanelDescriptor.userInitiatedProfileIndex(this.profile.title);
+
+    WebInspector.SidebarTreeElement.call(this, className, "", "", profile, false);
+
+    this.refreshTitles();
+}
+
+WebInspector.ProfileSidebarTreeElement.prototype = {
+    onselect: function()
+    {
+        if (!this._suppressOnSelect)
+            this.treeOutline.panel._showProfile(this.profile);
+    },
+
+    ondelete: function()
+    {
+        this.treeOutline.panel._removeProfileHeader(this.profile);
+        return true;
+    },
+
+    get mainTitle()
+    {
+        if (this._mainTitle)
+            return this._mainTitle;
+        if (WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(this.profile.title))
+            return WebInspector.UIString(this._titleFormat, this._profileNumber);
+        return this.profile.title;
+    },
+
+    set mainTitle(x)
+    {
+        this._mainTitle = x;
+        this.refreshTitles();
+    },
+
+    set searchMatches(matches)
+    {
+        if (!matches) {
+            if (!this.bubbleElement)
+                return;
+            this.bubbleElement.removeStyleClass("search-matches");
+            this.bubbleText = "";
+            return;
+        }
+
+        this.bubbleText = matches;
+        this.bubbleElement.addStyleClass("search-matches");
+    },
+
+    /**
+     * @param {!Event} event
+     * @param {!WebInspector.ProfilesPanel} panel
+     */
+    handleContextMenuEvent: function(event, panel)
+    {
+        var profile = this.profile;
+        var contextMenu = new WebInspector.ContextMenu(event);
+        // FIXME: use context menu provider
+        contextMenu.appendItem(WebInspector.UIString("Load\u2026"), panel._fileSelectorElement.click.bind(panel._fileSelectorElement));
+        if (profile.canSaveToFile())
+            contextMenu.appendItem(WebInspector.UIString("Save\u2026"), profile.saveToFile.bind(profile));
+        contextMenu.appendItem(WebInspector.UIString("Delete"), this.ondelete.bind(this));
+        contextMenu.show();
+    },
+
+    __proto__: WebInspector.SidebarTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarTreeElement}
+ * @param {WebInspector.ProfilesPanel} panel
+ * @param {string} title
+ * @param {string=} subtitle
+ */
+WebInspector.ProfileGroupSidebarTreeElement = function(panel, title, subtitle)
+{
+    WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true);
+    this._panel = panel;
+}
+
+WebInspector.ProfileGroupSidebarTreeElement.prototype = {
+    onselect: function()
+    {
+        if (this.children.length > 0)
+            this._panel._showProfile(this.children[this.children.length - 1].profile);
+    },
+
+    __proto__: WebInspector.SidebarTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarTreeElement}
+ * @param {!WebInspector.ProfilesPanel} panel
+ */
+WebInspector.ProfilesSidebarTreeElement = function(panel)
+{
+    this._panel = panel;
+    this.small = false;
+
+    WebInspector.SidebarTreeElement.call(this, "profile-launcher-view-tree-item", WebInspector.UIString("Profiles"), "", null, false);
+}
+
+WebInspector.ProfilesSidebarTreeElement.prototype = {
+    onselect: function()
+    {
+        this._panel._showLauncherView();
+    },
+
+    get selectable()
+    {
+        return true;
+    },
+
+    __proto__: WebInspector.SidebarTreeElement.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfilesPanel}
+ */
+WebInspector.CPUProfilerPanel = function()
+{
+    WebInspector.ProfilesPanel.call(this, "cpu-profiler", new WebInspector.CPUProfileType());
+}
+
+WebInspector.CPUProfilerPanel.prototype = {
+    __proto__: WebInspector.ProfilesPanel.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfilesPanel}
+ */
+WebInspector.CSSSelectorProfilerPanel = function()
+{
+    WebInspector.ProfilesPanel.call(this, "css-profiler", new WebInspector.CSSSelectorProfileType());
+}
+
+WebInspector.CSSSelectorProfilerPanel.prototype = {
+    __proto__: WebInspector.ProfilesPanel.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfilesPanel}
+ */
+WebInspector.HeapProfilerPanel = function()
+{
+    WebInspector.ProfilesPanel.call(this, "heap-profiler", new WebInspector.HeapSnapshotProfileType());
+}
+
+WebInspector.HeapProfilerPanel.prototype = {
+    __proto__: WebInspector.ProfilesPanel.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfilesPanel}
+ */
+WebInspector.CanvasProfilerPanel = function()
+{
+    WebInspector.ProfilesPanel.call(this, "canvas-profiler", new WebInspector.CanvasProfileType());
+}
+
+WebInspector.CanvasProfilerPanel.prototype = {
+    __proto__: WebInspector.ProfilesPanel.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfilesPanel}
+ */
+WebInspector.MemoryChartProfilerPanel = function()
+{
+    WebInspector.ProfilesPanel.call(this, "memory-chart-profiler", new WebInspector.NativeMemoryProfileType());
+}
+
+WebInspector.MemoryChartProfilerPanel.prototype = {
+    __proto__: WebInspector.ProfilesPanel.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfilesPanel}
+ */
+WebInspector.NativeMemoryProfilerPanel = function()
+{
+    WebInspector.ProfilesPanel.call(this, "memory-snapshot-profiler", new WebInspector.NativeSnapshotProfileType());
+}
+
+WebInspector.NativeMemoryProfilerPanel.prototype = {
+    __proto__: WebInspector.ProfilesPanel.prototype
+}
+
+
+importScript("ProfileDataGridTree.js");
+importScript("BottomUpProfileDataGridTree.js");
+importScript("CPUProfileView.js");
+importScript("CSSSelectorProfileView.js");
+importScript("FlameChart.js");
+importScript("HeapSnapshot.js");
+importScript("HeapSnapshotDataGrids.js");
+importScript("HeapSnapshotGridNodes.js");
+importScript("HeapSnapshotLoader.js");
+importScript("HeapSnapshotProxy.js");
+importScript("HeapSnapshotView.js");
+importScript("HeapSnapshotWorkerDispatcher.js");
+importScript("JSHeapSnapshot.js");
+importScript("NativeHeapSnapshot.js");
+importScript("NativeMemorySnapshotView.js");
+importScript("ProfileLauncherView.js");
+importScript("TopDownProfileDataGridTree.js");
+importScript("CanvasProfileView.js");
diff --git a/Source/devtools/front_end/ProfilesPanelDescriptor.js b/Source/devtools/front_end/ProfilesPanelDescriptor.js
new file mode 100644
index 0000000..978f3d1
--- /dev/null
+++ b/Source/devtools/front_end/ProfilesPanelDescriptor.js
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.PanelDescriptor}
+ */
+WebInspector.ProfilesPanelDescriptor = function()
+{
+    WebInspector.PanelDescriptor.call(this, "profiles", WebInspector.UIString("Profiles"), "ProfilesPanel", "ProfilesPanel.js");
+}
+
+WebInspector.ProfilesPanelDescriptor.prototype = {
+    registerShortcuts: function()
+    {
+        var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Profiles Panel"));
+        section.addAlternateKeys(WebInspector.ProfilesPanelDescriptor.ShortcutKeys.StartStopRecording, WebInspector.UIString("Start/stop recording"));
+    },
+
+    __proto__: WebInspector.PanelDescriptor.prototype
+}
+
+WebInspector.ProfilesPanelDescriptor.ShortcutKeys = {
+    StartStopRecording: [
+        WebInspector.KeyboardShortcut.makeDescriptor("e", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ]
+}
+
+WebInspector.ProfilesPanelDescriptor.ProfileURLRegExp = /webkit-profile:\/\/(.+)\/(.+)/;
+
+WebInspector.ProfilesPanelDescriptor.UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
+
+/**
+ * @param {string} title
+ * @return {boolean}
+ */
+WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile = function(title)
+{
+    return title.startsWith(WebInspector.ProfilesPanelDescriptor.UserInitiatedProfileName);
+}
+
+/**
+ * @param {string} title
+ * @return {number}
+ * @throws {string}
+ */
+WebInspector.ProfilesPanelDescriptor.userInitiatedProfileIndex = function(title)
+{
+    if (!WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(title))
+        throw "Not user-initiated profile title.";
+    var suffix = title.substring(WebInspector.ProfilesPanelDescriptor.UserInitiatedProfileName.length + 1);
+    return parseInt(suffix, 10);
+}
+
+/**
+ * @param {string} title
+ * @return {string}
+ */
+WebInspector.ProfilesPanelDescriptor.resolveProfileTitle = function(title)
+{
+    if (!WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(title))
+        return title;
+    return WebInspector.UIString("Profile %d", WebInspector.ProfilesPanelDescriptor.userInitiatedProfileIndex(title));
+}
+
+/**
+ * @param {Event} event
+ */
+WebInspector.ProfilesPanelDescriptor._openCPUProfile = function(event)
+{
+    event.preventDefault();
+    var panel = WebInspector.showPanel("profiles");
+    var link = /** @type {!Element} */ (event.target);
+    var view = /** @type {WebInspector.CPUProfileView} */ (panel.showProfile("CPU", link.profileUID));
+    if (!view)
+        return;
+    if (typeof link.timeLeft === "number" && typeof link.timeRight === "number")
+        view.selectRange(link.timeLeft, link.timeRight);
+}
+
+/**
+ * @param {number} uid
+ * @param {string} linkText
+ * @param {number=} timeLeft
+ * @param {number=} timeRight
+ * @param {string=} tooltipText
+ * @return {!Element}
+ */
+WebInspector.ProfilesPanelDescriptor.linkifyCPUProfile = function(uid, linkText, timeLeft, timeRight, tooltipText)
+{
+    var link = document.createElement("a");
+    link.innerText = linkText;
+    link.href = WebInspector.UIString("show CPU profile");
+    link.target = "_blank";
+    if (tooltipText)
+        link.title = tooltipText;
+    link.timeLeft = timeLeft;
+    link.timeRight = timeRight;
+    link.profileUID = uid;
+    link.addEventListener("click", WebInspector.ProfilesPanelDescriptor._openCPUProfile, true);
+    return link;
+}
diff --git a/Source/devtools/front_end/Progress.js b/Source/devtools/front_end/Progress.js
new file mode 100644
index 0000000..4cd0508
--- /dev/null
+++ b/Source/devtools/front_end/Progress.js
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.Progress = function()
+{
+}
+
+WebInspector.Progress.prototype = {
+    /**
+     * @param {number} totalWork
+     */
+    setTotalWork: function(totalWork) { },
+
+    /**
+     * @param {string} title
+     */
+    setTitle: function(title) { },
+
+    /**
+     * @param {number} worked
+     * @param {string=} title
+     */
+    setWorked: function(worked, title) { },
+
+    /**
+     * @param {number=} worked
+     */
+    worked: function(worked) { },
+
+    done: function() { },
+
+    /**
+     * @return {boolean}
+     */
+    isCanceled: function() { return false; }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.Progress} parent
+ */
+WebInspector.CompositeProgress = function(parent)
+{
+    this._parent = parent;
+    this._children = [];
+    this._childrenDone = 0;
+    this._parent.setTotalWork(1);
+    this._parent.setWorked(0);
+}
+
+WebInspector.CompositeProgress.prototype = {
+    _childDone: function()
+    {
+        if (++this._childrenDone === this._children.length)
+            this._parent.done();
+    },
+
+    /**
+     * @param {number=} weight
+     */
+    createSubProgress: function(weight)
+    {
+        var child = new WebInspector.SubProgress(this, weight);
+        this._children.push(child);
+        return child;
+    },
+
+    _update: function()
+    {
+        var totalWeights = 0;
+        var done = 0;
+
+        for (var i = 0; i < this._children.length; ++i) {
+            var child = this._children[i];
+            if (child._totalWork)
+                done += child._weight * child._worked / child._totalWork;
+            totalWeights += child._weight;
+        }
+        this._parent.setWorked(done / totalWeights);
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.Progress}
+ * @param {WebInspector.CompositeProgress} composite
+ * @param {number=} weight
+ */
+WebInspector.SubProgress = function(composite, weight)
+{
+    this._composite = composite;
+    this._weight = weight || 1;
+    this._worked = 0;
+}
+
+WebInspector.SubProgress.prototype = {
+    /**
+     * @return {boolean}
+     */
+    isCanceled: function()
+    {
+        return this._composite._parent.isCanceled();
+    },
+
+    /**
+     * @param {string} title
+     */
+    setTitle: function(title)
+    {
+        this._composite._parent.setTitle(title);
+    },
+
+    done: function()
+    {
+        this.setWorked(this._totalWork);
+        this._composite._childDone();
+    },
+
+    /**
+     * @param {number} totalWork
+     */
+    setTotalWork: function(totalWork)
+    {
+        this._totalWork = totalWork;
+        this._composite._update();
+    },
+
+    /**
+     * @param {number} worked
+     * @param {string=} title
+     */
+    setWorked: function(worked, title)
+    {
+        this._worked = worked;
+        if (typeof title !== "undefined")
+            this.setTitle(title);
+        this._composite._update();
+    },
+
+    /**
+     * @param {number=} worked
+     */
+    worked: function(worked)
+    {
+        this.setWorked(this._worked + (worked || 1));
+    }
+}
diff --git a/Source/devtools/front_end/ProgressIndicator.js b/Source/devtools/front_end/ProgressIndicator.js
new file mode 100644
index 0000000..cb9d2ef
--- /dev/null
+++ b/Source/devtools/front_end/ProgressIndicator.js
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.Progress}
+ * @extends {WebInspector.Object}
+ */
+WebInspector.ProgressIndicator = function()
+{
+    this.element = document.createElement("div");
+    this.element.className = "progress-bar-container";
+    this._labelElement = this.element.createChild("span");
+    this._progressElement = this.element.createChild("progress");
+    this._stopButton = new WebInspector.StatusBarButton(WebInspector.UIString("Cancel"), "progress-bar-stop-button");
+    this._stopButton.addEventListener("click", this.cancel, this);
+    this.element.appendChild(this._stopButton.element);
+    this._isCanceled = false;
+    this._worked = 0;
+}
+
+WebInspector.ProgressIndicator.Events = {
+    Done: "Done"
+}
+
+WebInspector.ProgressIndicator.prototype = {
+    /**
+     * @param {Element} parent
+     */
+    show: function(parent)
+    {
+        parent.appendChild(this.element);
+    },
+
+    hide: function()
+    {
+        var parent = this.element.parentElement;
+        if (parent)
+            parent.removeChild(this.element);
+    },
+
+    done: function()
+    {
+        if (this._isDone)
+            return;
+        this._isDone = true;
+        this.hide();
+        this.dispatchEventToListeners(WebInspector.ProgressIndicator.Events.Done);
+    },
+
+    cancel: function()
+    {
+        this._isCanceled = true;
+    },
+
+    isCanceled: function()
+    {
+        return this._isCanceled;
+    },
+
+    /**
+     * @param {string} title
+     */
+    setTitle: function(title)
+    {
+        this._labelElement.textContent = title;
+    },
+
+    /**
+     * @param {number} totalWork
+     */
+    setTotalWork: function(totalWork)
+    {
+        this._progressElement.max = totalWork;
+    },
+
+    /**
+     * @param {number} worked
+     * @param {string=} title
+     */
+    setWorked: function(worked, title)
+    {
+        this._worked = worked;
+        this._progressElement.value = worked;
+        if (title)
+            this.setTitle(title);
+    },
+
+    /**
+     * @param {number=} worked
+     */
+    worked: function(worked)
+    {
+        this.setWorked(this._worked + (worked || 1));
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
diff --git a/Source/devtools/front_end/PropertiesSection.js b/Source/devtools/front_end/PropertiesSection.js
new file mode 100644
index 0000000..3aa4fc4
--- /dev/null
+++ b/Source/devtools/front_end/PropertiesSection.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Section}
+ * @param {string|Element} title
+ * @param {string=} subtitle
+ */
+WebInspector.PropertiesSection = function(title, subtitle)
+{
+    WebInspector.Section.call(this, title, subtitle);
+
+    this.headerElement.addStyleClass("monospace");
+    this.propertiesElement = document.createElement("ol");
+    this.propertiesElement.className = "properties properties-tree monospace";
+    this.propertiesTreeOutline = new TreeOutline(this.propertiesElement, true);
+    this.propertiesTreeOutline.setFocusable(false);
+    this.propertiesTreeOutline.section = this;
+
+    this.element.appendChild(this.propertiesElement);
+}
+
+WebInspector.PropertiesSection.prototype = {
+    __proto__: WebInspector.Section.prototype
+}
diff --git a/Source/devtools/front_end/PropertiesSidebarPane.js b/Source/devtools/front_end/PropertiesSidebarPane.js
new file mode 100644
index 0000000..0f7bbf6
--- /dev/null
+++ b/Source/devtools/front_end/PropertiesSidebarPane.js
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2007 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.PropertiesSidebarPane = function()
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Properties"));
+}
+
+WebInspector.PropertiesSidebarPane._objectGroupName = "properties-sidebar-pane";
+
+WebInspector.PropertiesSidebarPane.prototype = {
+    update: function(node)
+    {
+        var body = this.bodyElement;
+
+        if (!node) {
+            body.removeChildren();
+            this.sections = [];
+            return;
+        }
+
+        WebInspector.RemoteObject.resolveNode(node, WebInspector.PropertiesSidebarPane._objectGroupName, nodeResolved.bind(this));
+
+        function nodeResolved(object)
+        {
+            if (!object)
+                return;
+            function protoList()
+            {
+                var proto = this;
+                var result = {};
+                var counter = 1;
+                while (proto) {
+                    result[counter++] = proto;
+                    proto = proto.__proto__;
+                }
+                return result;
+            }
+            object.callFunction(protoList, undefined, nodePrototypesReady.bind(this));
+            object.release();
+        }
+
+        function nodePrototypesReady(object)
+        {
+            if (!object)
+                return;
+            object.getOwnProperties(fillSection.bind(this));
+        }
+
+        function fillSection(prototypes)
+        {
+            if (!prototypes)
+                return;
+
+            var body = this.bodyElement;
+            body.removeChildren();
+            this.sections = [];
+
+            // Get array of prototype user-friendly names.
+            for (var i = 0; i < prototypes.length; ++i) {
+                if (!parseInt(prototypes[i].name, 10))
+                    continue;
+
+                var prototype = prototypes[i].value;
+                var title = prototype.description;
+                if (title.match(/Prototype$/))
+                    title = title.replace(/Prototype$/, "");
+                var section = new WebInspector.ObjectPropertiesSection(prototype, title);
+                this.sections.push(section);
+                body.appendChild(section.element);
+            }
+        }
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
diff --git a/Source/devtools/front_end/RawSourceCode.js b/Source/devtools/front_end/RawSourceCode.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Source/devtools/front_end/RawSourceCode.js
diff --git a/Source/devtools/front_end/RemoteObject.js b/Source/devtools/front_end/RemoteObject.js
new file mode 100644
index 0000000..ef71cd8
--- /dev/null
+++ b/Source/devtools/front_end/RemoteObject.js
@@ -0,0 +1,701 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {string|undefined} objectId
+ * @param {string} type
+ * @param {string|undefined} subtype
+ * @param {*} value
+ * @param {string=} description
+ * @param {RuntimeAgent.ObjectPreview=} preview
+ */
+WebInspector.RemoteObject = function(objectId, type, subtype, value, description, preview)
+{
+    this._type = type;
+    this._subtype = subtype;
+    if (objectId) {
+        // handle
+        this._objectId = objectId;
+        this._description = description;
+        this._hasChildren = true;
+        this._preview = preview;
+    } else {
+        // Primitive or null object.
+        console.assert(type !== "object" || value === null);
+        this._description = description || (value + "");
+        this._hasChildren = false;
+        this.value = value;
+    }
+}
+
+/**
+ * @param {number|string|boolean} value
+ * @return {WebInspector.RemoteObject}
+ */
+WebInspector.RemoteObject.fromPrimitiveValue = function(value)
+{
+    return new WebInspector.RemoteObject(undefined, typeof value, undefined, value);
+}
+
+/**
+ * @param {*} value
+ * @return {WebInspector.RemoteObject}
+ */
+WebInspector.RemoteObject.fromLocalObject = function(value)
+{
+    return new WebInspector.LocalJSONObject(value);
+}
+
+/**
+ * @param {WebInspector.DOMNode} node
+ * @param {string} objectGroup
+ * @param {function(?WebInspector.RemoteObject)} callback
+ */
+WebInspector.RemoteObject.resolveNode = function(node, objectGroup, callback)
+{
+    /**
+     * @param {?Protocol.Error} error
+     * @param {RuntimeAgent.RemoteObject} object
+     */
+    function mycallback(error, object)
+    {
+        if (!callback)
+            return;
+
+        if (error || !object)
+            callback(null);
+        else
+            callback(WebInspector.RemoteObject.fromPayload(object));
+    }
+    DOMAgent.resolveNode(node.id, objectGroup, mycallback);
+}
+
+/**
+ * @param {RuntimeAgent.RemoteObject=} payload
+ * @return {WebInspector.RemoteObject}
+ */
+WebInspector.RemoteObject.fromPayload = function(payload)
+{
+    console.assert(typeof payload === "object", "Remote object payload should only be an object");
+
+    return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
+}
+
+/**
+ * @param {WebInspector.RemoteObject} remoteObject
+ * @return {string}
+ */
+WebInspector.RemoteObject.type = function(remoteObject)
+{
+    if (remoteObject === null)
+        return "null";
+
+    var type = typeof remoteObject;
+    if (type !== "object" && type !== "function")
+        return type;
+
+    return remoteObject.type;
+}
+
+WebInspector.RemoteObject.prototype = {
+    /** @return {RuntimeAgent.RemoteObjectId} */
+    get objectId()
+    {
+        return this._objectId;
+    },
+
+    /** @return {string} */
+    get type()
+    {
+        return this._type;
+    },
+
+    /** @return {string|undefined} */
+    get subtype()
+    {
+        return this._subtype;
+    },
+
+    /** @return {string|undefined} */
+    get description()
+    {
+        return this._description;
+    },
+
+    /** @return {boolean} */
+    get hasChildren()
+    {
+        return this._hasChildren;
+    },
+
+    /** @return {RuntimeAgent.ObjectPreview|undefined} */
+    get preview()
+    {
+        return this._preview;
+    },
+
+    /**
+     * @param {function(Array.<WebInspector.RemoteObjectProperty>, Array.<WebInspector.RemoteObjectProperty>=)} callback
+     */
+    getOwnProperties: function(callback)
+    {
+        this.doGetProperties(true, callback);
+    },
+
+    /**
+     * @param {function(Array.<WebInspector.RemoteObjectProperty>, Array.<WebInspector.RemoteObjectProperty>=)} callback
+     */
+    getAllProperties: function(callback)
+    {
+        this.doGetProperties(false, callback);
+    },
+
+    /**
+     * @param {boolean} ownProperties
+     * @param {function(Array.<WebInspector.RemoteObjectProperty>, Array.<WebInspector.RemoteObjectProperty>=)} callback
+     */
+    doGetProperties: function(ownProperties, callback)
+    {
+        if (!this._objectId) {
+            callback([]);
+            return;
+        }
+
+        /**
+         * @param {?Protocol.Error} error
+         * @param {Array.<RuntimeAgent.PropertyDescriptor>} properties
+         * @param {Array.<RuntimeAgent.InternalPropertyDescriptor>=} internalProperties
+         */
+        function remoteObjectBinder(error, properties, internalProperties)
+        {
+            if (error) {
+                callback(null);
+                return;
+            }
+            var result = [];
+            for (var i = 0; properties && i < properties.length; ++i) {
+                var property = properties[i];
+                if (property.get || property.set) {
+                    if (property.get)
+                        result.push(new WebInspector.RemoteObjectProperty("get " + property.name, WebInspector.RemoteObject.fromPayload(property.get), property));
+                    if (property.set)
+                        result.push(new WebInspector.RemoteObjectProperty("set " + property.name, WebInspector.RemoteObject.fromPayload(property.set), property));
+                } else
+                    result.push(new WebInspector.RemoteObjectProperty(property.name, WebInspector.RemoteObject.fromPayload(property.value), property));
+            }
+            var internalPropertiesResult;
+            if (internalProperties) {
+                internalPropertiesResult = [];
+                for (var i = 0; i < internalProperties.length; i++) {
+                    var property = internalProperties[i];
+                    internalPropertiesResult.push(new WebInspector.RemoteObjectProperty(property.name, WebInspector.RemoteObject.fromPayload(property.value)));
+                }
+            }
+            callback(result, internalPropertiesResult);
+        }
+        RuntimeAgent.getProperties(this._objectId, ownProperties, remoteObjectBinder);
+    },
+
+    /**
+     * @param {string} name
+     * @param {string} value
+     * @param {function(string=)} callback
+     */
+    setPropertyValue: function(name, value, callback)
+    {
+        if (!this._objectId) {
+            callback("Can't set a property of non-object.");
+            return;
+        }
+
+        RuntimeAgent.evaluate.invoke({expression:value, doNotPauseOnExceptionsAndMuteConsole:true}, evaluatedCallback.bind(this));
+
+        /**
+         * @param {?Protocol.Error} error
+         * @param {RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function evaluatedCallback(error, result, wasThrown)
+        {
+            if (error || wasThrown) {
+                callback(error || result.description);
+                return;
+            }
+
+            this.doSetObjectPropertyValue(result, name, callback);
+
+            if (result._objectId)
+                RuntimeAgent.releaseObject(result._objectId);
+        }
+    },
+
+    /**
+     * @param {RuntimeAgent.RemoteObject} result
+     * @param {string} name
+     * @param {function(string=)} callback
+     */
+    doSetObjectPropertyValue: function(result, name, callback)
+    {
+        // Note that it is not that simple with accessor properties. The proto object may contain the property,
+        // however not the proto object must be 'this', but the main object.
+        var setPropertyValueFunction = "function(a, b) { this[a] = b; }";
+
+        // Special case for NaN, Infinity and -Infinity
+        if (result.type === "number" && typeof result.value !== "number")
+            setPropertyValueFunction = "function(a) { this[a] = " + result.description + "; }";
+
+        delete result.description; // Optimize on traffic.
+        RuntimeAgent.callFunctionOn(this._objectId, setPropertyValueFunction, [{ value:name }, result], true, undefined, undefined, propertySetCallback.bind(this));
+
+        /**
+         * @param {?Protocol.Error} error
+         * @param {RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function propertySetCallback(error, result, wasThrown)
+        {
+            if (error || wasThrown) {
+                callback(error || result.description);
+                return;
+            }
+            callback();
+        }
+    },
+
+    /**
+     * @param {function(?DOMAgent.NodeId)} callback
+     */
+    pushNodeToFrontend: function(callback)
+    {
+        if (this._objectId)
+            WebInspector.domAgent.pushNodeToFrontend(this._objectId, callback);
+        else
+            callback(0);
+    },
+
+    highlightAsDOMNode: function()
+    {
+        WebInspector.domAgent.highlightDOMNode(undefined, undefined, this._objectId);
+    },
+
+    hideDOMNodeHighlight: function()
+    {
+        WebInspector.domAgent.hideDOMNodeHighlight();
+    },
+
+    /**
+     * @param {function(this:Object)} functionDeclaration
+     * @param {Array.<RuntimeAgent.CallArgument>=} args
+     * @param {function(?WebInspector.RemoteObject)=} callback
+     */
+    callFunction: function(functionDeclaration, args, callback)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function mycallback(error, result, wasThrown)
+        {
+            if (!callback)
+                return;
+
+            callback((error || wasThrown) ? null : WebInspector.RemoteObject.fromPayload(result));
+        }
+
+        RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, undefined, undefined, mycallback);
+    },
+
+    /**
+     * @param {function(this:Object)} functionDeclaration
+     * @param {Array.<RuntimeAgent.CallArgument>|undefined} args
+     * @param {function(*)} callback
+     */
+    callFunctionJSON: function(functionDeclaration, args, callback)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function mycallback(error, result, wasThrown)
+        {
+            callback((error || wasThrown) ? null : result.value);
+        }
+
+        RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, true, false, mycallback);
+    },
+
+    release: function()
+    {
+        if (!this._objectId)
+            return;
+        RuntimeAgent.releaseObject(this._objectId);
+    },
+
+    /**
+     * @return {number}
+     */
+    arrayLength: function()
+    {
+        if (this.subtype !== "array")
+            return 0;
+
+        var matches = this._description.match(/\[([0-9]+)\]/);
+        if (!matches)
+            return 0;
+        return parseInt(matches[1], 10);
+    }
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.RemoteObject}
+ * @param {string|undefined} objectId
+ * @param {WebInspector.ScopeRef} scopeRef
+ * @param {string} type
+ * @param {string|undefined} subtype
+ * @param {*} value
+ * @param {string=} description
+ * @param {RuntimeAgent.ObjectPreview=} preview
+ */
+WebInspector.ScopeRemoteObject = function(objectId, scopeRef, type, subtype, value, description, preview)
+{
+    WebInspector.RemoteObject.call(this, objectId, type, subtype, value, description, preview);
+    this._scopeRef = scopeRef;
+    this._savedScopeProperties = undefined;
+};
+
+/**
+ * @param {RuntimeAgent.RemoteObject} payload
+ * @param {WebInspector.ScopeRef=} scopeRef
+ * @return {WebInspector.RemoteObject}
+ */
+WebInspector.ScopeRemoteObject.fromPayload = function(payload, scopeRef)
+{
+    if (scopeRef)
+        return new WebInspector.ScopeRemoteObject(payload.objectId, scopeRef, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
+    else
+        return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
+}
+
+WebInspector.ScopeRemoteObject.prototype = {
+    /**
+     * @param {boolean} ownProperties
+     * @param {function(Array.<WebInspector.RemoteObjectProperty>, Array.<WebInspector.RemoteObjectProperty>=)} callback
+     * @override
+     */
+    doGetProperties: function(ownProperties, callback)
+    {
+        if (this._savedScopeProperties) {
+            // No need to reload scope variables, as the remote object never
+            // changes its properties. If variable is updated, the properties
+            // array is patched locally.
+            callback(this._savedScopeProperties.slice(), []);
+            return;
+        }
+
+        /**
+         * @param {Array.<WebInspector.RemoteObjectProperty>} properties
+         * @param {Array.<WebInspector.RemoteObjectProperty>=} internalProperties
+         */
+        function wrappedCallback(properties, internalProperties)
+        {
+            if (this._scopeRef && properties instanceof Array)
+                this._savedScopeProperties = properties.slice();
+            callback(properties, internalProperties);
+        }
+
+        WebInspector.RemoteObject.prototype.doGetProperties.call(this, ownProperties, wrappedCallback.bind(this));
+    },
+
+    /**
+     * @override
+     * @param {RuntimeAgent.RemoteObject} result
+     * @param {string} name
+     * @param {function(string=)} callback
+     */
+    doSetObjectPropertyValue: function(result, name, callback)
+    {
+        var newValue;
+
+        switch (result.type) {
+            case "undefined":
+                newValue = {};
+                break;
+            case "object":
+            case "function":
+                newValue = { objectId: result.objectId };
+                break;
+            default:
+                newValue = { value: result.value };
+        }
+
+        DebuggerAgent.setVariableValue(this._scopeRef.number, name, newValue, this._scopeRef.callFrameId, this._scopeRef.functionId, setVariableValueCallback.bind(this));
+
+        /**
+         * @param {?Protocol.Error} error
+         */
+        function setVariableValueCallback(error)
+        {
+            if (error) {
+                callback(error);
+                return;
+            }
+            if (this._savedScopeProperties) {
+                for (var i = 0; i < this._savedScopeProperties.length; i++) {
+                    if (this._savedScopeProperties[i].name === name)
+                        this._savedScopeProperties[i].value = WebInspector.RemoteObject.fromPayload(result);
+                }
+            }
+            callback();
+        }
+    },
+
+    __proto__: WebInspector.RemoteObject.prototype
+};
+
+/**
+ * Either callFrameId or functionId (exactly one) must be defined.
+ * @constructor
+ * @param {number} number
+ * @param {string=} callFrameId
+ * @param {string=} functionId
+ */
+WebInspector.ScopeRef = function(number, callFrameId, functionId)
+{
+    this.number = number;
+    this.callFrameId = callFrameId;
+    this.functionId = functionId;
+}
+
+/**
+ * @constructor
+ * @param {string} name
+ * @param {WebInspector.RemoteObject} value
+ * @param {Object=} descriptor
+ */
+WebInspector.RemoteObjectProperty = function(name, value, descriptor)
+{
+    this.name = name;
+    this.value = value;
+    this.enumerable = descriptor ? !!descriptor.enumerable : true;
+    this.writable = descriptor ? !!descriptor.writable : true;
+    if (descriptor && descriptor.wasThrown)
+        this.wasThrown = true;
+}
+
+/**
+ * @param {string} name
+ * @param {string} value
+ * @return {WebInspector.RemoteObjectProperty}
+ */
+WebInspector.RemoteObjectProperty.fromPrimitiveValue = function(name, value)
+{
+    return new WebInspector.RemoteObjectProperty(name, WebInspector.RemoteObject.fromPrimitiveValue(value));
+}
+
+/**
+ * @param {string} name
+ * @param {WebInspector.RemoteObject} value
+ * @return {WebInspector.RemoteObjectProperty}
+ */
+WebInspector.RemoteObjectProperty.fromScopeValue = function(name, value)
+{
+    var result = new WebInspector.RemoteObjectProperty(name, value);
+    result.writable = false;
+    return result;
+}
+
+// The below is a wrapper around a local object that provides an interface comaptible
+// with RemoteObject, to be used by the UI code (primarily ObjectPropertiesSection).
+// Note that only JSON-compliant objects are currently supported, as there's no provision
+// for traversing prototypes, extracting class names via constuctor, handling properties
+// or functions.
+
+/**
+ * @constructor
+ * @extends {WebInspector.RemoteObject}
+ * @param {*} value
+ */
+WebInspector.LocalJSONObject = function(value)
+{
+    this._value = value;
+}
+
+WebInspector.LocalJSONObject.prototype = {
+    /**
+     * @return {string}
+     */
+    get description()
+    {
+        if (this._cachedDescription)
+            return this._cachedDescription;
+
+        if (this.type === "object") {
+            switch (this.subtype) {
+            case "array":
+                function formatArrayItem(property)
+                {
+                    return property.value.description;
+                }
+                this._cachedDescription = this._concatenate("[", "]", formatArrayItem);
+                break;
+            case "date":
+                this._cachedDescription = "" + this._value;
+                break;
+            case "null":
+                this._cachedDescription = "null";
+                break;
+            default:
+                function formatObjectItem(property)
+                {
+                    return property.name + ":" + property.value.description;
+                }
+                this._cachedDescription = this._concatenate("{", "}", formatObjectItem);
+            }
+        } else
+            this._cachedDescription = String(this._value);
+
+        return this._cachedDescription;
+    },
+
+    /**
+     * @param {string} prefix
+     * @param {string} suffix
+     * @return {string}
+     */
+    _concatenate: function(prefix, suffix, formatProperty)
+    {
+        const previewChars = 100;
+
+        var buffer = prefix;
+        var children = this._children();
+        for (var i = 0; i < children.length; ++i) {
+            var itemDescription = formatProperty(children[i]);
+            if (buffer.length + itemDescription.length > previewChars) {
+                buffer += ",\u2026";
+                break;
+            }
+            if (i)
+                buffer += ", ";
+            buffer += itemDescription;
+        }
+        buffer += suffix;
+        return buffer;
+    },
+
+    /**
+     * @return {string}
+     */
+    get type()
+    {
+        return typeof this._value;
+    },
+
+    /**
+     * @return {string|undefined}
+     */
+    get subtype()
+    {
+        if (this._value === null)
+            return "null";
+
+        if (this._value instanceof Array)
+            return "array";
+
+        if (this._value instanceof Date)
+            return "date";
+
+        return undefined;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    get hasChildren()
+    {
+        if ((typeof this._value !== "object") || (this._value === null))
+            return false;
+        return !!Object.keys(/** @type {!Object} */ (this._value)).length;
+    },
+
+    /**
+     * @param {function(Array.<WebInspector.RemoteObjectProperty>)} callback
+     */
+    getOwnProperties: function(callback)
+    {
+        callback(this._children());
+    },
+
+    /**
+     * @param {function(Array.<WebInspector.RemoteObjectProperty>)} callback
+     */
+    getAllProperties: function(callback)
+    {
+        callback(this._children());
+    },
+
+    /**
+     * @return {Array.<WebInspector.RemoteObjectProperty>}
+     */
+    _children: function()
+    {
+        if (!this.hasChildren)
+            return [];
+        var value = /** @type {!Object} */ (this._value);
+
+        function buildProperty(propName)
+        {
+            return new WebInspector.RemoteObjectProperty(propName, new WebInspector.LocalJSONObject(this._value[propName]));
+        }
+        if (!this._cachedChildren)
+            this._cachedChildren = Object.keys(value).map(buildProperty.bind(this));
+        return this._cachedChildren;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isError: function()
+    {
+        return false;
+    },
+
+    /**
+     * @return {number}
+     */
+    arrayLength: function()
+    {
+        return this._value instanceof Array ? this._value.length : 0;
+    }
+}
diff --git a/Source/devtools/front_end/RequestCookiesView.js b/Source/devtools/front_end/RequestCookiesView.js
new file mode 100644
index 0000000..20e82ac
--- /dev/null
+++ b/Source/devtools/front_end/RequestCookiesView.js
@@ -0,0 +1,94 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestCookiesView = function(request)
+{
+    WebInspector.View.call(this);
+    this.element.addStyleClass("resource-cookies-view");
+
+    this._request = request;
+}
+
+WebInspector.RequestCookiesView.prototype = {
+    wasShown: function()
+    {
+        this._request.addEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshCookies, this);
+        this._request.addEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshCookies, this);
+
+        if (!this._gotCookies) {
+            if (!this._emptyView) {
+                this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("This request has no cookies."));
+                this._emptyView.show(this.element);
+            }
+            return;
+        }
+
+        if (!this._cookiesTable)
+            this._buildCookiesTable();
+    },
+
+    willHide: function()
+    {
+        this._request.removeEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshCookies, this);
+        this._request.removeEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshCookies, this);
+    },
+
+    get _gotCookies()
+    {
+        return (this._request.requestCookies && this._request.requestCookies.length) || (this._request.responseCookies && this._request.responseCookies.length);
+    },
+
+    _buildCookiesTable: function()
+    {
+        this.detachChildViews();
+
+        this._cookiesTable = new WebInspector.CookiesTable(true);
+        this._cookiesTable.setCookieFolders([
+            {folderName: WebInspector.UIString("Request Cookies"), cookies: this._request.requestCookies},
+            {folderName: WebInspector.UIString("Response Cookies"), cookies: this._request.responseCookies}
+        ]);
+        this._cookiesTable.show(this.element);
+    },
+
+    _refreshCookies: function()
+    {
+        delete this._cookiesTable;
+        if (!this._gotCookies || !this.isShowing())
+            return;
+        this._buildCookiesTable();
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/RequestHTMLView.js b/Source/devtools/front_end/RequestHTMLView.js
new file mode 100644
index 0000000..63facf3
--- /dev/null
+++ b/Source/devtools/front_end/RequestHTMLView.js
@@ -0,0 +1,72 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.RequestView}
+ * @param {WebInspector.NetworkRequest} request
+ * @param {string} dataURL
+ */
+WebInspector.RequestHTMLView = function(request, dataURL)
+{
+    WebInspector.RequestView.call(this, request);
+    this._dataURL = dataURL;
+    this.element.addStyleClass("html");
+}
+
+WebInspector.RequestHTMLView.prototype = {
+    hasContent: function()
+    {
+        return true;
+    },
+
+    wasShown: function()
+    {
+        this._createIFrame();
+    },
+
+    willHide: function(parentElement)
+    {
+        this.element.removeChildren();
+    },
+
+    _createIFrame: function()
+    {
+        // We need to create iframe again each time because contentDocument
+        // is deleted when iframe is removed from its parent.
+        this.element.removeChildren();
+        var iframe = document.createElement("iframe");
+        iframe.setAttribute("sandbox", ""); // Forbid to run JavaScript and set unique origin.
+        iframe.setAttribute("src", this._dataURL);
+        this.element.appendChild(iframe);
+    },
+
+    __proto__: WebInspector.RequestView.prototype
+}
diff --git a/Source/devtools/front_end/RequestHeadersView.js b/Source/devtools/front_end/RequestHeadersView.js
new file mode 100644
index 0000000..33ade13
--- /dev/null
+++ b/Source/devtools/front_end/RequestHeadersView.js
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) IBM Corp. 2009  All rights reserved.
+ * Copyright (C) 2010 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestHeadersView = function(request)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("resourceView.css");
+    this.element.addStyleClass("resource-headers-view");
+
+    this._request = request;
+
+    this._headersListElement = document.createElement("ol");
+    this._headersListElement.className = "outline-disclosure";
+    this.element.appendChild(this._headersListElement);
+
+    this._headersTreeOutline = new TreeOutline(this._headersListElement);
+    this._headersTreeOutline.expandTreeElementsWhenArrowing = true;
+
+    this._urlTreeElement = new TreeElement("", null, false);
+    this._urlTreeElement.selectable = false;
+    this._headersTreeOutline.appendChild(this._urlTreeElement);
+
+    this._requestMethodTreeElement = new TreeElement("", null, false);
+    this._requestMethodTreeElement.selectable = false;
+    this._headersTreeOutline.appendChild(this._requestMethodTreeElement);
+
+    this._statusCodeTreeElement = new TreeElement("", null, false);
+    this._statusCodeTreeElement.selectable = false;
+    this._headersTreeOutline.appendChild(this._statusCodeTreeElement);
+
+    this._requestHeadersTreeElement = new TreeElement("", null, true);
+    this._requestHeadersTreeElement.expanded = true;
+    this._requestHeadersTreeElement.selectable = false;
+    this._headersTreeOutline.appendChild(this._requestHeadersTreeElement);
+
+    this._decodeRequestParameters = true;
+
+    this._showRequestHeadersText = false;
+    this._showResponseHeadersText = false;
+
+    this._queryStringTreeElement = new TreeElement("", null, true);
+    this._queryStringTreeElement.expanded = true;
+    this._queryStringTreeElement.selectable = false;
+    this._queryStringTreeElement.hidden = true;
+    this._headersTreeOutline.appendChild(this._queryStringTreeElement);
+
+    this._urlFragmentTreeElement = new TreeElement("", null, true);
+    this._urlFragmentTreeElement.expanded = true;
+    this._urlFragmentTreeElement.selectable = false;
+    this._urlFragmentTreeElement.hidden = true;
+    this._headersTreeOutline.appendChild(this._urlFragmentTreeElement);
+
+    this._formDataTreeElement = new TreeElement("", null, true);
+    this._formDataTreeElement.expanded = true;
+    this._formDataTreeElement.selectable = false;
+    this._formDataTreeElement.hidden = true;
+    this._headersTreeOutline.appendChild(this._formDataTreeElement);
+
+    this._requestPayloadTreeElement = new TreeElement(WebInspector.UIString("Request Payload"), null, true);
+    this._requestPayloadTreeElement.expanded = true;
+    this._requestPayloadTreeElement.selectable = false;
+    this._requestPayloadTreeElement.hidden = true;
+    this._headersTreeOutline.appendChild(this._requestPayloadTreeElement);
+
+    this._responseHeadersTreeElement = new TreeElement("", null, true);
+    this._responseHeadersTreeElement.expanded = true;
+    this._responseHeadersTreeElement.selectable = false;
+    this._headersTreeOutline.appendChild(this._responseHeadersTreeElement);
+}
+
+WebInspector.RequestHeadersView.prototype = {
+
+    wasShown: function()
+    {
+        this._request.addEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this);
+        this._request.addEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this);
+        this._request.addEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this);
+
+        this._refreshURL();
+        this._refreshQueryString();
+        this._refreshUrlFragment();
+        this._refreshRequestHeaders();
+        this._refreshResponseHeaders();
+        this._refreshHTTPInformation();
+    },
+
+    willHide: function()
+    {
+        this._request.removeEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this);
+        this._request.removeEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this);
+        this._request.removeEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this);
+    },
+
+    /**
+     * @param {string} name
+     * @param {string} value
+     */
+    _formatHeader: function(name, value)
+    {
+        var fragment = document.createDocumentFragment();
+        fragment.createChild("div", "header-name").textContent = name + ":";
+        fragment.createChild("div", "header-value source-code").textContent = value;
+
+        return fragment;
+    },
+
+    /**
+     * @param {string} value
+     * @param {string} className
+     * @param {boolean} decodeParameters
+     */
+    _formatParameter: function(value, className, decodeParameters)
+    {
+        var errorDecoding = false;
+
+        if (decodeParameters) {
+            value = value.replace(/\+/g, " ");
+            if (value.indexOf("%") >= 0) {
+                try {
+                    value = decodeURIComponent(value);
+                } catch (e) {
+                    errorDecoding = true;
+                }
+            }
+        }
+        var div = document.createElement("div");
+        div.className = className;
+        if (errorDecoding)
+            div.createChild("span", "error-message").textContent = WebInspector.UIString("(unable to decode value)");
+        else
+            div.textContent = value;
+        return div;
+    },
+
+    _refreshURL: function()
+    {
+        this._urlTreeElement.title = this._formatHeader(WebInspector.UIString("Request URL"), this._request.url);
+    },
+
+    _refreshQueryString: function()
+    {
+        var queryString = this._request.queryString();
+        var queryParameters = this._request.queryParameters;
+        this._queryStringTreeElement.hidden = !queryParameters;
+        if (queryParameters)
+            this._refreshParams(WebInspector.UIString("Query String Parameters"), queryParameters, queryString, this._queryStringTreeElement);
+    },
+
+    _refreshUrlFragment: function()
+    {
+        var urlFragment = this._request.parsedURL.fragment;
+        this._urlFragmentTreeElement.hidden = !urlFragment;
+
+        if (!urlFragment)
+            return;
+
+        var sectionTitle = WebInspector.UIString("URL fragment");
+
+        this._urlFragmentTreeElement.removeChildren();
+        this._urlFragmentTreeElement.listItemElement.removeChildren();
+        this._urlFragmentTreeElement.listItemElement.appendChild(document.createTextNode(sectionTitle));
+
+        var fragmentTreeElement = new TreeElement(null, null, false);
+        fragmentTreeElement.title = this._formatHeader("#", urlFragment);
+        fragmentTreeElement.selectable = false;
+        this._urlFragmentTreeElement.appendChild(fragmentTreeElement);
+    },
+
+    _refreshFormData: function()
+    {
+        this._formDataTreeElement.hidden = true;
+        this._requestPayloadTreeElement.hidden = true;
+
+        var formData = this._request.requestFormData;
+        if (!formData)
+            return;
+
+        var formParameters = this._request.formParameters;
+        if (formParameters) {
+            this._formDataTreeElement.hidden = false;
+            this._refreshParams(WebInspector.UIString("Form Data"), formParameters, formData, this._formDataTreeElement);
+        } else {
+            this._requestPayloadTreeElement.hidden = false;
+            try {
+                var json = JSON.parse(formData);
+                this._refreshRequestJSONPayload(json, formData, false);
+            } catch (e) {
+                this._populateTreeElementWithSourceText(this._requestPayloadTreeElement, formData);
+            }
+        }
+    },
+
+    _populateTreeElementWithSourceText: function(treeElement, sourceText)
+    {
+        treeElement.removeChildren();
+
+        var sourceTreeElement = new TreeElement(null, null, false);
+        sourceTreeElement.selectable = false;
+        treeElement.appendChild(sourceTreeElement);
+
+        var sourceTextElement = document.createElement("span");
+        sourceTextElement.addStyleClass("header-value");
+        sourceTextElement.addStyleClass("source-code");
+        sourceTextElement.textContent = String(sourceText).trim();
+        sourceTreeElement.listItemElement.appendChild(sourceTextElement);
+    },
+
+    _refreshParams: function(title, params, sourceText, paramsTreeElement)
+    {
+        paramsTreeElement.removeChildren();
+
+        paramsTreeElement.listItemElement.removeChildren();
+        paramsTreeElement.listItemElement.appendChild(document.createTextNode(title));
+
+        var headerCount = document.createElement("span");
+        headerCount.addStyleClass("header-count");
+        headerCount.textContent = WebInspector.UIString(" (%d)", params.length);
+        paramsTreeElement.listItemElement.appendChild(headerCount);
+
+        function toggleViewSource()
+        {
+            paramsTreeElement._viewSource = !paramsTreeElement._viewSource;
+            this._refreshParams(title, params, sourceText, paramsTreeElement);
+        }
+
+        paramsTreeElement.listItemElement.appendChild(this._createViewSourceToggle(paramsTreeElement._viewSource, toggleViewSource.bind(this)));
+        
+        if (paramsTreeElement._viewSource) {
+            this._populateTreeElementWithSourceText(paramsTreeElement, sourceText);
+            return;
+        }
+
+        var toggleTitle = this._decodeRequestParameters ? WebInspector.UIString("view URL encoded") : WebInspector.UIString("view decoded");
+        var toggleButton = this._createToggleButton(toggleTitle);
+        toggleButton.addEventListener("click", this._toggleURLDecoding.bind(this));
+        paramsTreeElement.listItemElement.appendChild(toggleButton);
+
+        for (var i = 0; i < params.length; ++i) {
+            var paramNameValue = document.createDocumentFragment();
+            var name = this._formatParameter(params[i].name + ":", "header-name", this._decodeRequestParameters);
+            var value = this._formatParameter(params[i].value, "header-value source-code", this._decodeRequestParameters);
+            paramNameValue.appendChild(name);
+            paramNameValue.appendChild(value);
+
+            var parmTreeElement = new TreeElement(paramNameValue, null, false);
+            parmTreeElement.selectable = false;
+            paramsTreeElement.appendChild(parmTreeElement);
+        }
+    },
+
+    /**
+     * @param {*} parsedObject
+     * @param {string} sourceText
+     * @param {boolean} viewSource
+     */
+    _refreshRequestJSONPayload: function(parsedObject, sourceText, viewSource)
+    {
+        this._requestPayloadTreeElement.removeChildren();
+
+        var listItem = this._requestPayloadTreeElement.listItemElement;
+        listItem.removeChildren();
+        listItem.appendChild(document.createTextNode(this._requestPayloadTreeElement.title));
+
+        var setViewSource = this._refreshRequestJSONPayload.bind(this, parsedObject, sourceText);
+
+        if (viewSource) {
+            listItem.appendChild(this._createViewSourceToggle(true, setViewSource.bind(this, false)));
+            this._populateTreeElementWithSourceText(this._requestPayloadTreeElement, sourceText);
+        } else {
+            listItem.appendChild(this._createViewSourceToggle(false, setViewSource.bind(this, true)));
+            var object = WebInspector.RemoteObject.fromLocalObject(parsedObject);
+            var section = new WebInspector.ObjectPropertiesSection(object, object.description);
+            section.expand();
+            section.editable = false;
+            listItem.appendChild(section.element);
+        }
+    },
+
+    /**
+     * @param {boolean} viewSource
+     * @param {Function} handler
+     */
+    _createViewSourceToggle: function(viewSource, handler)
+    {
+        var viewSourceToggleTitle = viewSource ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
+        var viewSourceToggleButton = this._createToggleButton(viewSourceToggleTitle);
+        viewSourceToggleButton.addEventListener("click", handler);
+        return viewSourceToggleButton;
+    },
+
+    _toggleURLDecoding: function(event)
+    {
+        this._decodeRequestParameters = !this._decodeRequestParameters;
+        this._refreshQueryString();
+        this._refreshFormData();
+    },
+
+    _getHeaderValue: function(headers, key)
+    {
+        var lowerKey = key.toLowerCase();
+        for (var testKey in headers) {
+            if (testKey.toLowerCase() === lowerKey)
+                return headers[testKey];
+        }
+    },
+
+    _refreshRequestHeaders: function()
+    {
+        if (this._showRequestHeadersText)
+            this._refreshHeadersText(WebInspector.UIString("Request Headers"), this._request.sortedRequestHeaders, this._request.requestHeadersText, this._requestHeadersTreeElement);
+        else
+            this._refreshHeaders(WebInspector.UIString("Request Headers"), this._request.sortedRequestHeaders, this._requestHeadersTreeElement);
+
+        if (this._request.requestHeadersText) {
+            var toggleButton = this._createHeadersToggleButton(this._showRequestHeadersText);
+            toggleButton.addEventListener("click", this._toggleRequestHeadersText.bind(this));
+            this._requestHeadersTreeElement.listItemElement.appendChild(toggleButton);
+        }
+
+        this._refreshFormData();
+    },
+
+    _refreshResponseHeaders: function()
+    {
+        if (this._showResponseHeadersText)
+            this._refreshHeadersText(WebInspector.UIString("Response Headers"), this._request.sortedResponseHeaders, this._request.responseHeadersText, this._responseHeadersTreeElement);
+        else
+            this._refreshHeaders(WebInspector.UIString("Response Headers"), this._request.sortedResponseHeaders, this._responseHeadersTreeElement);
+
+        if (this._request.responseHeadersText) {
+            var toggleButton = this._createHeadersToggleButton(this._showResponseHeadersText);
+            toggleButton.addEventListener("click", this._toggleResponseHeadersText.bind(this));
+            this._responseHeadersTreeElement.listItemElement.appendChild(toggleButton);
+        }
+    },
+
+    _refreshHTTPInformation: function()
+    {
+        var requestMethodElement = this._requestMethodTreeElement;
+        requestMethodElement.hidden = !this._request.statusCode;
+        var statusCodeElement = this._statusCodeTreeElement;
+        statusCodeElement.hidden = !this._request.statusCode;
+
+        if (this._request.statusCode) {
+            var statusCodeFragment = document.createDocumentFragment();
+            statusCodeFragment.createChild("div", "header-name").textContent = WebInspector.UIString("Status Code") + ":";
+
+            var statusCodeImage = statusCodeFragment.createChild("div", "resource-status-image");
+            statusCodeImage.title = this._request.statusCode + " " + this._request.statusText;
+
+            if (this._request.statusCode < 300 || this._request.statusCode === 304)
+                statusCodeImage.addStyleClass("green-ball");
+            else if (this._request.statusCode < 400)
+                statusCodeImage.addStyleClass("orange-ball");
+            else
+                statusCodeImage.addStyleClass("red-ball");
+
+            requestMethodElement.title = this._formatHeader(WebInspector.UIString("Request Method"), this._request.requestMethod);
+
+            var value = statusCodeFragment.createChild("div", "header-value source-code");
+            value.textContent = this._request.statusCode + " " + this._request.statusText;
+            if (this._request.cached)
+                value.createChild("span", "status-from-cache").textContent = " " + WebInspector.UIString("(from cache)");
+
+            statusCodeElement.title = statusCodeFragment;
+        }
+    },
+
+    _refreshHeadersTitle: function(title, headersTreeElement, headersLength)
+    {
+        headersTreeElement.listItemElement.removeChildren();
+        headersTreeElement.listItemElement.appendChild(document.createTextNode(title));
+
+        var headerCount = document.createElement("span");
+        headerCount.addStyleClass("header-count");
+        headerCount.textContent = WebInspector.UIString(" (%d)", headersLength);
+        headersTreeElement.listItemElement.appendChild(headerCount);
+    },
+
+    _refreshHeaders: function(title, headers, headersTreeElement)
+    {
+        headersTreeElement.removeChildren();
+
+        var length = headers.length;
+        this._refreshHeadersTitle(title, headersTreeElement, length);
+        headersTreeElement.hidden = !length;
+        for (var i = 0; i < length; ++i) {
+            var headerTreeElement = new TreeElement(null, null, false);
+            headerTreeElement.title = this._formatHeader(headers[i].name, headers[i].value);
+            headerTreeElement.selectable = false;
+            headersTreeElement.appendChild(headerTreeElement);
+        }
+    },
+
+    _refreshHeadersText: function(title, headers, headersText, headersTreeElement)
+    {
+        this._populateTreeElementWithSourceText(headersTreeElement, headersText);
+        this._refreshHeadersTitle(title, headersTreeElement, headers.length);
+    },
+
+    _toggleRequestHeadersText: function(event)
+    {
+        this._showRequestHeadersText = !this._showRequestHeadersText;
+        this._refreshRequestHeaders();
+    },
+
+    _toggleResponseHeadersText: function(event)
+    {
+        this._showResponseHeadersText = !this._showResponseHeadersText;
+        this._refreshResponseHeaders();
+    },
+
+    _createToggleButton: function(title)
+    {
+        var button = document.createElement("span");
+        button.addStyleClass("header-toggle");
+        button.textContent = title;
+        return button;
+    },
+
+    _createHeadersToggleButton: function(isHeadersTextShown)
+    {
+        var toggleTitle = isHeadersTextShown ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
+        return this._createToggleButton(toggleTitle);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/RequestJSONView.js b/Source/devtools/front_end/RequestJSONView.js
new file mode 100644
index 0000000..56d5c5b
--- /dev/null
+++ b/Source/devtools/front_end/RequestJSONView.js
@@ -0,0 +1,116 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.RequestView}
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestJSONView = function(request, parsedJSON)
+{
+    WebInspector.RequestView.call(this, request);
+    this._parsedJSON = parsedJSON;
+    this.element.addStyleClass("json");
+}
+
+WebInspector.RequestJSONView.parseJSON = function(text)
+{
+    var prefix = "";
+
+    // Trim while(1), for(;;), weird numbers, etc. We need JSON start.
+    var start = /[{[]/.exec(text);
+    if (start && start.index) {
+        prefix = text.substring(0, start.index);
+        text = text.substring(start.index);
+    }
+
+    try {
+        return new WebInspector.ParsedJSON(JSON.parse(text), prefix, "");
+    } catch (e) {
+        return;
+    }
+}
+
+WebInspector.RequestJSONView.parseJSONP = function(text)
+{
+    // Taking everything between first and last parentheses
+    var start = text.indexOf("(");
+    var end = text.lastIndexOf(")");
+    if (start == -1 || end == -1 || end < start)
+        return;
+
+    var prefix = text.substring(0, start + 1);
+    var suffix = text.substring(end);
+    text = text.substring(start + 1, end);
+
+    try {
+        return new WebInspector.ParsedJSON(JSON.parse(text), prefix, suffix);
+    } catch (e) {
+        return;
+    }
+}
+
+WebInspector.RequestJSONView.prototype = {
+    hasContent: function()
+    {
+        return true;
+    },
+
+    wasShown: function()
+    {
+        this._initialize();
+    },
+
+    _initialize: function()
+    {
+        if (this._initialized)
+            return;
+        this._initialized = true;
+
+        var obj = WebInspector.RemoteObject.fromLocalObject(this._parsedJSON.data);
+        var title = this._parsedJSON.prefix + obj.description + this._parsedJSON.suffix;
+        var section = new WebInspector.ObjectPropertiesSection(obj, title);
+        section.expand();
+        section.editable = false;
+        this.element.appendChild(section.element);
+    },
+
+    __proto__: WebInspector.RequestView.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.ParsedJSON = function(data, prefix, suffix)
+{
+    this.data = data;
+    this.prefix = prefix;
+    this.suffix = suffix;
+}
diff --git a/Source/devtools/front_end/RequestPreviewView.js b/Source/devtools/front_end/RequestPreviewView.js
new file mode 100644
index 0000000..b0188c5
--- /dev/null
+++ b/Source/devtools/front_end/RequestPreviewView.js
@@ -0,0 +1,121 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.RequestContentView}
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestPreviewView = function(request, responseView)
+{
+    WebInspector.RequestContentView.call(this, request);
+    this._responseView = responseView;
+}
+
+WebInspector.RequestPreviewView.prototype = {
+    contentLoaded: function()
+    {
+        if (!this.request.content) {
+            if (!this._emptyView) {
+                this._emptyView = this._createEmptyView();
+                this._emptyView.show(this.element);
+                this.innerView = this._emptyView;
+            }
+        } else {
+            if (this._emptyView) {
+                this._emptyView.detach();
+                delete this._emptyView;
+            }
+
+            if (!this._previewView)
+                this._previewView = this._createPreviewView();
+            this._previewView.show(this.element);
+            this.innerView = this._previewView;
+        }
+    },
+
+    _createEmptyView: function()
+    {
+        return new WebInspector.EmptyView(WebInspector.UIString("This request has no preview available."));
+    },
+
+    _jsonView: function()
+    {
+        var parsedJSON = WebInspector.RequestJSONView.parseJSON(this.request.content);
+        if (parsedJSON)
+            return new WebInspector.RequestJSONView(this.request, parsedJSON);
+    },
+
+    _htmlView: function()
+    {
+        var dataURL = this.request.asDataURL();
+        if (dataURL !== null)
+            return new WebInspector.RequestHTMLView(this.request, dataURL);
+    },
+
+    _createPreviewView: function()
+    {
+        if (this.request.content) {
+            if (this.request.hasErrorStatusCode()) {
+                var htmlView = this._htmlView();
+                if (htmlView)
+                    return htmlView;
+            }
+
+            if (this.request.type === WebInspector.resourceTypes.XHR) {
+                var jsonView = this._jsonView();
+                if (jsonView)
+                    return jsonView;
+            }
+
+            if (this.request.type === WebInspector.resourceTypes.XHR && this.request.mimeType === "text/html") {
+                var htmlView = this._htmlView();
+                if (htmlView)
+                    return htmlView;
+            }
+
+            if (this.request.type === WebInspector.resourceTypes.Script && this.request.mimeType === "application/json") {
+                var jsonView = this._jsonView();
+                if (jsonView)
+                    return jsonView;
+            }
+        }
+
+        if (this._responseView.sourceView)
+            return this._responseView.sourceView;
+
+        if (this.request.type === WebInspector.resourceTypes.Other)
+            return this._createEmptyView();
+
+        return WebInspector.RequestView.nonSourceViewForRequest(this.request);
+    },
+
+    __proto__: WebInspector.RequestContentView.prototype
+}
diff --git a/Source/devtools/front_end/RequestResponseView.js b/Source/devtools/front_end/RequestResponseView.js
new file mode 100644
index 0000000..de55197
--- /dev/null
+++ b/Source/devtools/front_end/RequestResponseView.js
@@ -0,0 +1,71 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.RequestContentView}
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestResponseView = function(request)
+{
+    WebInspector.RequestContentView.call(this, request);
+}
+
+WebInspector.RequestResponseView._maxFormattedResourceSize = 100000;
+
+WebInspector.RequestResponseView.prototype = {
+    get sourceView()
+    {
+        if (!this._sourceView && WebInspector.RequestView.hasTextContent(this.request))
+            this._sourceView = this.request.resourceSize < WebInspector.RequestResponseView._maxFormattedResourceSize ? new WebInspector.ResourceSourceFrame(this.request) : new WebInspector.ResourceSourceFrameFallback(this.request);
+        return this._sourceView;
+    },
+
+    contentLoaded: function()
+    {
+        if (!this.request.content || !this.sourceView) {
+            if (!this._emptyView) {
+                this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("This request has no response data available."));
+                this._emptyView.show(this.element);
+                this.innerView = this._emptyView;
+            }
+        } else {
+            if (this._emptyView) {
+                this._emptyView.detach();
+                delete this._emptyView;
+            }
+
+            this.sourceView.show(this.element);
+            this.innerView = this.sourceView;
+        }
+    },
+
+    __proto__: WebInspector.RequestContentView.prototype
+}
diff --git a/Source/devtools/front_end/RequestTimingView.js b/Source/devtools/front_end/RequestTimingView.js
new file mode 100644
index 0000000..63d07e5
--- /dev/null
+++ b/Source/devtools/front_end/RequestTimingView.js
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestTimingView = function(request)
+{
+    WebInspector.View.call(this);
+    this.element.addStyleClass("resource-timing-view");
+
+    this._request = request;
+}
+
+WebInspector.RequestTimingView.prototype = {
+    wasShown: function()
+    {
+        this._request.addEventListener(WebInspector.NetworkRequest.Events.TimingChanged, this._refresh, this);
+
+        if (!this._request.timing) {
+            if (!this._emptyView) {
+                this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("This request has no detailed timing info."));
+                this._emptyView.show(this.element);
+                this.innerView = this._emptyView;
+            }
+            return;
+        }
+
+        if (this._emptyView) {
+            this._emptyView.detach();
+            delete this._emptyView;
+        }
+
+        this._refresh();
+    },
+
+    willHide: function()
+    {
+        this._request.removeEventListener(WebInspector.NetworkRequest.Events.TimingChanged, this._refresh, this);
+    },
+
+    _refresh: function()
+    {
+        if (this._tableElement)
+            this._tableElement.parentElement.removeChild(this._tableElement);
+
+        this._tableElement = WebInspector.RequestTimingView.createTimingTable(this._request);
+        this.element.appendChild(this._tableElement);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+
+WebInspector.RequestTimingView.createTimingTable = function(request)
+{
+    var tableElement = document.createElement("table");
+    var rows = [];
+
+    function addRow(title, className, start, end)
+    {
+        var row = {};
+        row.title = title;
+        row.className = className;
+        row.start = start;
+        row.end = end;
+        rows.push(row);
+    }
+
+    if (request.timing.proxyStart !== -1)
+        addRow(WebInspector.UIString("Proxy"), "proxy", request.timing.proxyStart, request.timing.proxyEnd);
+
+    if (request.timing.dnsStart !== -1)
+        addRow(WebInspector.UIString("DNS Lookup"), "dns", request.timing.dnsStart, request.timing.dnsEnd);
+
+    if (request.timing.connectStart !== -1) {
+        if (request.connectionReused)
+            addRow(WebInspector.UIString("Blocking"), "connecting", request.timing.connectStart, request.timing.connectEnd);
+        else {
+            var connectStart = request.timing.connectStart;
+            // Connection includes DNS, subtract it here.
+            if (request.timing.dnsStart !== -1)
+                connectStart += request.timing.dnsEnd - request.timing.dnsStart;
+            addRow(WebInspector.UIString("Connecting"), "connecting", connectStart, request.timing.connectEnd);
+        }
+    }
+
+    if (request.timing.sslStart !== -1)
+        addRow(WebInspector.UIString("SSL"), "ssl", request.timing.sslStart, request.timing.sslEnd);
+
+    var sendStart = request.timing.sendStart;
+    if (request.timing.sslStart !== -1)
+        sendStart += request.timing.sslEnd - request.timing.sslStart;
+
+    addRow(WebInspector.UIString("Sending"), "sending", request.timing.sendStart, request.timing.sendEnd);
+    addRow(WebInspector.UIString("Waiting"), "waiting", request.timing.sendEnd, request.timing.receiveHeadersEnd);
+    addRow(WebInspector.UIString("Receiving"), "receiving", (request.responseReceivedTime - request.timing.requestTime) * 1000, (request.endTime - request.timing.requestTime) * 1000);
+
+    const chartWidth = 200;
+    var total = (request.endTime - request.timing.requestTime) * 1000;
+    var scale = chartWidth / total;
+
+    for (var i = 0; i < rows.length; ++i) {
+        var tr = document.createElement("tr");
+        tableElement.appendChild(tr);
+
+        var td = document.createElement("td");
+        td.textContent = rows[i].title;
+        tr.appendChild(td);
+
+        td = document.createElement("td");
+        td.width = chartWidth + "px";
+
+        var row = document.createElement("div");
+        row.className = "network-timing-row";
+        td.appendChild(row);
+
+        var bar = document.createElement("span");
+        bar.className = "network-timing-bar " + rows[i].className;
+        bar.style.left = scale * rows[i].start + "px";
+        bar.style.right = scale * (total - rows[i].end) + "px";
+        bar.style.backgroundColor = rows[i].color;
+        bar.textContent = "\u200B"; // Important for 0-time items to have 0 width.
+        row.appendChild(bar);
+
+        var title = document.createElement("span");
+        title.className = "network-timing-bar-title";
+        if (total - rows[i].end < rows[i].start)
+            title.style.right = (scale * (total - rows[i].end) + 3) + "px";
+        else
+            title.style.left = (scale * rows[i].start + 3) + "px";
+        title.textContent = Number.secondsToString((rows[i].end - rows[i].start) / 1000);
+        row.appendChild(title);
+
+        tr.appendChild(td);
+    }
+    return tableElement;
+}
diff --git a/Source/devtools/front_end/RequestView.js b/Source/devtools/front_end/RequestView.js
new file mode 100644
index 0000000..359e3eb
--- /dev/null
+++ b/Source/devtools/front_end/RequestView.js
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestView = function(request)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("resourceView.css");
+
+    this.element.addStyleClass("resource-view");
+    this.request = request;
+}
+
+WebInspector.RequestView.prototype = {
+    hasContent: function()
+    {
+        return false;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestView.hasTextContent = function(request)
+{
+    if (request.type.isTextType())
+        return true; 
+    if (request.type === WebInspector.resourceTypes.Other || request.hasErrorStatusCode())
+        return request.content && !request.contentEncoded;
+    return false;
+}
+
+/**
+ * @param {WebInspector.NetworkRequest} request
+ */
+WebInspector.RequestView.nonSourceViewForRequest = function(request)
+{
+    switch (request.type) {
+    case WebInspector.resourceTypes.Image:
+        return new WebInspector.ImageView(request);
+    case WebInspector.resourceTypes.Font:
+        return new WebInspector.FontView(request);
+    default:
+        return new WebInspector.RequestView(request);
+    }
+}
diff --git a/Source/devtools/front_end/Resource.js b/Source/devtools/front_end/Resource.js
new file mode 100644
index 0000000..4b2b399
--- /dev/null
+++ b/Source/devtools/front_end/Resource.js
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2007, 2008 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @implements {WebInspector.ContentProvider}
+ * @param {?WebInspector.NetworkRequest} request
+ * @param {string} url
+ * @param {string} documentURL
+ * @param {NetworkAgent.FrameId} frameId
+ * @param {NetworkAgent.LoaderId} loaderId
+ * @param {WebInspector.ResourceType} type
+ * @param {string} mimeType
+ * @param {boolean=} isHidden
+ */
+WebInspector.Resource = function(request, url, documentURL, frameId, loaderId, type, mimeType, isHidden)
+{
+    this._request = request;
+    this.url = url;
+    this._documentURL = documentURL;
+    this._frameId = frameId;
+    this._loaderId = loaderId;
+    this._type = type || WebInspector.resourceTypes.Other;
+    this._mimeType = mimeType;
+    this._isHidden = isHidden;
+
+    /** @type {?string} */ this._content;
+    /** @type {boolean} */ this._contentEncoded;
+    this._pendingContentCallbacks = [];
+    if (this._request && !this._request.finished)
+        this._request.addEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._requestFinished, this);
+}
+
+WebInspector.Resource.Events = {
+    MessageAdded: "message-added",
+    MessagesCleared: "messages-cleared",
+}
+
+WebInspector.Resource.prototype = {
+    /**
+     * @return {?WebInspector.NetworkRequest}
+     */
+    get request()
+    {
+        return this._request;
+    },
+
+    /**
+     * @return {string}
+     */
+    get url()
+    {
+        return this._url;
+    },
+
+    set url(x)
+    {
+        this._url = x;
+        this._parsedURL = new WebInspector.ParsedURL(x);
+    },
+
+    get parsedURL()
+    {
+        return this._parsedURL;
+    },
+
+    /**
+     * @return {string}
+     */
+    get documentURL()
+    {
+        return this._documentURL;
+    },
+
+    /**
+     * @return {NetworkAgent.FrameId}
+     */
+    get frameId()
+    {
+        return this._frameId;
+    },
+
+    /**
+     * @return {NetworkAgent.LoaderId}
+     */
+    get loaderId()
+    {
+        return this._loaderId;
+    },
+
+    /**
+     * @return {string}
+     */
+    get displayName()
+    {
+        return this._parsedURL.displayName;
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    get type()
+    {
+        return this._request ? this._request.type : this._type;
+    },
+
+    /**
+     * @return {string}
+     */
+    get mimeType()
+    {
+        return this._request ? this._request.mimeType : this._mimeType;
+    },
+
+    /**
+     * @return {Array.<WebInspector.ConsoleMessage>}
+     */
+    get messages()
+    {
+        return this._messages || [];
+    },
+
+    /**
+     * @param {WebInspector.ConsoleMessage} msg
+     */
+    addMessage: function(msg)
+    {
+        if (!msg.isErrorOrWarning() || !msg.message)
+            return;
+
+        if (!this._messages)
+            this._messages = [];
+        this._messages.push(msg);
+        this.dispatchEventToListeners(WebInspector.Resource.Events.MessageAdded, msg);
+    },
+
+    /**
+     * @return {number}
+     */
+    get errors()
+    {
+        return this._errors || 0;
+    },
+
+    set errors(x)
+    {
+        this._errors = x;
+    },
+
+    /**
+     * @return {number}
+     */
+    get warnings()
+    {
+        return this._warnings || 0;
+    },
+
+    set warnings(x)
+    {
+        this._warnings = x;
+    },
+
+    clearErrorsAndWarnings: function()
+    {
+        this._messages = [];
+        this._warnings = 0;
+        this._errors = 0;
+        this.dispatchEventToListeners(WebInspector.Resource.Events.MessagesCleared);
+    },
+
+    /**
+     * @return {?string}
+     */
+    get content()
+    {
+        return this._content;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    get contentEncoded()
+    {
+        return this._contentEncoded;
+    },
+
+    /**
+     * @return {string}
+     */
+    contentURL: function()
+    {
+        return this._url;
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function()
+    {
+        return this.type;
+    },
+
+    /**
+     * @param {function(?string, boolean, string)} callback
+     */
+    requestContent: function(callback)
+    {
+        if (typeof this._content !== "undefined") {
+            callback(this._content, !!this._contentEncoded, this.canonicalMimeType());
+            return;
+        }
+
+        this._pendingContentCallbacks.push(callback);
+        if (!this._request || this._request.finished)
+            this._innerRequestContent();
+    },
+
+    canonicalMimeType: function()
+    {
+        return this.type.canonicalMimeType() || this.mimeType;
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback)
+    {
+        /**
+         * @param {?Protocol.Error} error
+         * @param {Array.<PageAgent.SearchMatch>} searchMatches
+         */
+        function callbackWrapper(error, searchMatches)
+        {
+            callback(searchMatches || []);
+        }
+
+        if (this.frameId)
+            PageAgent.searchInResource(this.frameId, this.url, query, caseSensitive, isRegex, callbackWrapper);
+        else
+            callback([]);
+    },
+
+    /**
+     * @param {Element} image
+     */
+    populateImageSource: function(image)
+    {
+        function onResourceContent()
+        {
+            var imageSrc = WebInspector.contentAsDataURL(this._content, this.mimeType, this._contentEncoded);
+            if (imageSrc === null)
+                imageSrc = this.url;
+            image.src = imageSrc;
+        }
+
+        this.requestContent(onResourceContent.bind(this));
+    },
+
+    _requestFinished: function()
+    {
+        this._request.removeEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._requestFinished, this);
+        if (this._pendingContentCallbacks.length)
+            this._innerRequestContent();
+    },
+
+
+    _innerRequestContent: function()
+    {
+        if (this._contentRequested)
+            return;
+        this._contentRequested = true;
+
+        /**
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         */
+        function contentLoaded(content, contentEncoded)
+        {
+            this._content = content;
+            this._contentEncoded = contentEncoded;
+            var callbacks = this._pendingContentCallbacks.slice();
+            for (var i = 0; i < callbacks.length; ++i)
+                callbacks[i](this._content, this._contentEncoded, this.canonicalMimeType());
+            this._pendingContentCallbacks.length = 0;
+            delete this._contentRequested;
+        }
+
+        /**
+         * @param {?Protocol.Error} error
+         * @param {string} content
+         * @param {boolean} contentEncoded
+         */
+        function resourceContentLoaded(error, content, contentEncoded)
+        {
+            if (error)
+                console.error("Resource content request failed: " + error);
+            contentLoaded.call(this, error ? null : content, contentEncoded);
+        }
+        
+        if (this.request) {
+            /**
+             * @param {?string} content
+             * @param {boolean} contentEncoded
+             * @param {string} mimeType
+             */
+            function requestContentLoaded(content, contentEncoded, mimeType)
+            {
+                contentLoaded.call(this, content, contentEncoded);
+            }
+            
+            this.request.requestContent(requestContentLoaded.bind(this));
+            return;
+        }
+        PageAgent.getResourceContent(this.frameId, this.url, resourceContentLoaded.bind(this));
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isHidden: function()
+    {
+        return !!this._isHidden;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
diff --git a/Source/devtools/front_end/ResourceScriptMapping.js b/Source/devtools/front_end/ResourceScriptMapping.js
new file mode 100644
index 0000000..faaa369
--- /dev/null
+++ b/Source/devtools/front_end/ResourceScriptMapping.js
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.ScriptSourceMapping}
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.ResourceScriptMapping = function(workspace)
+{
+    this._workspace = workspace;
+    this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this);
+
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
+    this._initialize();
+}
+
+WebInspector.ResourceScriptMapping.prototype = {
+    /**
+     * @param {WebInspector.RawLocation} rawLocation
+     * @return {WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(rawLocation)
+    {
+        var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation);
+        var script = WebInspector.debuggerModel.scriptForId(debuggerModelLocation.scriptId);
+        var uiSourceCode = this._workspaceUISourceCodeForScript(script);
+        if (!uiSourceCode)
+            return null;
+        var scriptFile = uiSourceCode.scriptFile();
+        if (scriptFile && ((scriptFile.hasDivergedFromVM() && !scriptFile.isMergingToVM()) || scriptFile.isDivergingFromVM()))
+            return null;
+        return new WebInspector.UILocation(uiSourceCode, debuggerModelLocation.lineNumber, debuggerModelLocation.columnNumber || 0);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.DebuggerModel.Location}
+     */
+    uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
+    {
+        var scripts = this._scriptsForUISourceCode(uiSourceCode);
+        console.assert(scripts.length);
+        return WebInspector.debuggerModel.createRawLocation(scripts[0], lineNumber, columnNumber);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isIdentity: function()
+    {
+        return true;
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     */
+    addScript: function(script)
+    {
+        if (script.isAnonymousScript() || script.isDynamicScript())
+            return;
+        script.pushSourceMapping(this);
+        
+        var scriptsForSourceURL = script.isInlineScript() ? this._inlineScriptsForSourceURL : this._nonInlineScriptsForSourceURL;
+        scriptsForSourceURL[script.sourceURL] = scriptsForSourceURL[script.sourceURL] || [];
+        scriptsForSourceURL[script.sourceURL].push(script);
+
+        var uiSourceCode = this._workspaceUISourceCodeForScript(script);
+        if (!uiSourceCode)
+            return;
+
+        this._bindUISourceCodeToScripts(uiSourceCode, [script]);
+    },
+
+    _uiSourceCodeAddedToWorkspace: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        if (!uiSourceCode.url)
+            return;
+
+        var scripts = this._scriptsForUISourceCode(uiSourceCode);
+        if (!scripts.length)
+            return;
+
+        this._bindUISourceCodeToScripts(uiSourceCode, scripts);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _hasMergedToVM: function(uiSourceCode)
+    {
+        var scripts = this._scriptsForUISourceCode(uiSourceCode);
+        if (!scripts.length)
+            return;
+        for (var i = 0; i < scripts.length; ++i)
+            scripts[i].updateLocations();
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _hasDivergedFromVM: function(uiSourceCode)
+    {
+        var scripts = this._scriptsForUISourceCode(uiSourceCode);
+        if (!scripts.length)
+            return;
+        for (var i = 0; i < scripts.length; ++i)
+            scripts[i].updateLocations();
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     * @return {WebInspector.UISourceCode}
+     */
+    _workspaceUISourceCodeForScript: function(script)
+    {
+        if (script.isAnonymousScript() || script.isDynamicScript())
+            return null;
+        // FIXME: workaround for script.isDynamicScript() being unreliable.
+        if (!script.isInlineScript() && this._inlineScriptsForSourceURL[script.sourceURL])
+            return null;
+        return this._workspace.uiSourceCodeForURL(script.sourceURL);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {Array.<WebInspector.Script>}
+     */
+    _scriptsForUISourceCode: function(uiSourceCode)
+    {
+        var isInlineScript;
+        switch (uiSourceCode.contentType()) {
+        case WebInspector.resourceTypes.Document:
+            isInlineScript = true;
+            break;
+        case WebInspector.resourceTypes.Script:
+            isInlineScript = false;
+            break;
+        default:
+            return [];
+        }
+        if (!uiSourceCode.url)
+            return [];
+        var scriptsForSourceURL = isInlineScript ? this._inlineScriptsForSourceURL : this._nonInlineScriptsForSourceURL;
+        return scriptsForSourceURL[uiSourceCode.url] || [];
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {Array.<WebInspector.Script>} scripts
+     */
+    _bindUISourceCodeToScripts: function(uiSourceCode, scripts)
+    {
+        console.assert(scripts.length);
+        var scriptFile = new WebInspector.ResourceScriptFile(this, uiSourceCode, scripts);
+        uiSourceCode.setScriptFile(scriptFile);
+        for (var i = 0; i < scripts.length; ++i)
+            scripts[i].updateLocations();
+        uiSourceCode.setSourceMapping(this);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {Array.<WebInspector.Script>} scripts
+     */
+    _unbindUISourceCodeFromScripts: function(uiSourceCode, scripts)
+    {
+        console.assert(scripts.length);
+        var scriptFile = /** @type {WebInspector.ResourceScriptFile} */ (uiSourceCode.scriptFile());
+        scriptFile.dispose();
+        uiSourceCode.setScriptFile(null);
+        uiSourceCode.setSourceMapping(null);
+    },
+
+    _initialize: function()
+    {
+        /** @type {!Object.<string, !Array.<!WebInspector.Script>>} */
+        this._inlineScriptsForSourceURL = {};
+        /** @type {!Object.<string, !Array.<!WebInspector.Script>>} */
+        this._nonInlineScriptsForSourceURL = {};
+    },
+
+    _debuggerReset: function()
+    {
+        /**
+         * @param {!Object.<string, !Array.<!WebInspector.UISourceCode>>} scriptsForSourceURL
+         */
+        function unbindUISourceCodes(scriptsForSourceURL)
+        {
+            for (var sourceURL in scriptsForSourceURL) {
+                var scripts = scriptsForSourceURL[sourceURL];
+                if (!scripts.length)
+                    continue;
+                var uiSourceCode = this._workspaceUISourceCodeForScript(scripts[0]);
+                if (!uiSourceCode)
+                    continue;
+                this._unbindUISourceCodeFromScripts(uiSourceCode, scripts);
+            }
+        }
+
+        unbindUISourceCodes.call(this, this._inlineScriptsForSourceURL);
+        unbindUISourceCodes.call(this, this._nonInlineScriptsForSourceURL);
+        this._initialize();
+    },
+}
+
+/**
+ * @interface
+ * @extends {WebInspector.EventTarget}
+ */
+WebInspector.ScriptFile = function()
+{
+}
+
+WebInspector.ScriptFile.Events = {
+    DidMergeToVM: "DidMergeToVM",
+    DidDivergeFromVM: "DidDivergeFromVM",
+}
+
+WebInspector.ScriptFile.prototype = {
+    /**
+     * @return {boolean}
+     */
+    hasDivergedFromVM: function() { return false; },
+
+    /**
+     * @return {boolean}
+     */
+    isDivergingFromVM: function() { return false; },
+
+    /**
+     * @return {boolean}
+     */
+    isMergingToVM: function() { return false; },
+
+    checkMapping: function() { },
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ScriptFile}
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.ResourceScriptMapping} resourceScriptMapping
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ */
+WebInspector.ResourceScriptFile = function(resourceScriptMapping, uiSourceCode, scripts)
+{
+    console.assert(scripts.length);
+
+    WebInspector.ScriptFile.call(this);
+    this._resourceScriptMapping = resourceScriptMapping;
+    this._uiSourceCode = uiSourceCode;
+
+    if (this._uiSourceCode.contentType() === WebInspector.resourceTypes.Script)
+        this._script = scripts[0];
+
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
+    this._update();
+}
+
+WebInspector.ResourceScriptFile.prototype = {
+    _workingCopyCommitted: function(event)
+    {
+        /**
+         * @param {?string} error
+         */
+        function innerCallback(error)
+        {
+            if (error) {
+                this._update();
+                WebInspector.showErrorMessage(error);
+                return;
+            }
+
+            this._scriptSource = source;
+            this._update();
+        }
+        if (!this._script)
+            return;
+        var source = this._uiSourceCode.workingCopy();
+        if (this._script.hasSourceURL && !this._sourceEndsWithSourceURL(source))
+            source += "\n //@ sourceURL=" + this._script.sourceURL;
+        WebInspector.debuggerModel.setScriptSource(this._script.scriptId, source, innerCallback.bind(this));
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _isDiverged: function()
+    {
+        if (this._uiSourceCode.formatted())
+            return false;
+        if (this._uiSourceCode.isDirty())
+            return true;
+        if (!this._script)
+            return false;
+        if (typeof this._scriptSource === "undefined")
+            return false;
+        return !this._sourceMatchesScriptSource(this._uiSourceCode.workingCopy(), this._scriptSource);
+    },
+
+    /**
+     * @param {string} source
+     * @param {string} scriptSource
+     * @return {boolean}
+     */
+    _sourceMatchesScriptSource: function(source, scriptSource)
+    {
+        if (!scriptSource.startsWith(source))
+            return false;
+        var scriptSourceTail = scriptSource.substr(source.length).trim();
+        return !scriptSourceTail || !!scriptSourceTail.match(/^\/\/@\ssourceURL=\s*(\S*?)\s*$/m);
+    },
+
+    /**
+     * @param {string} source
+     * @return {boolean}
+     */
+    _sourceEndsWithSourceURL: function(source)
+    {
+        return !!source.match(/\/\/@\ssourceURL=\s*(\S*?)\s*$/m);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _workingCopyChanged: function(event)
+    {
+        this._update();
+    },
+
+    _update: function()
+    {
+        if (this._isDiverged() && !this._hasDivergedFromVM)
+            this._divergeFromVM();
+        else if (!this._isDiverged() && this._hasDivergedFromVM)
+            this._mergeToVM();
+    },
+
+    _divergeFromVM: function()
+    {
+        this._isDivergingFromVM = true;
+        this._resourceScriptMapping._hasDivergedFromVM(this._uiSourceCode);
+        delete this._isDivergingFromVM;
+        this._hasDivergedFromVM = true;
+        this.dispatchEventToListeners(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._uiSourceCode);
+    },
+
+    _mergeToVM: function()
+    {
+        delete this._hasDivergedFromVM;
+        this._isMergingToVM = true;
+        this._resourceScriptMapping._hasMergedToVM(this._uiSourceCode);
+        delete this._isMergingToVM;
+        this.dispatchEventToListeners(WebInspector.ScriptFile.Events.DidMergeToVM, this._uiSourceCode);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    hasDivergedFromVM: function()
+    {
+        return this._hasDivergedFromVM;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isDivergingFromVM: function()
+    {
+        return this._isDivergingFromVM;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isMergingToVM: function()
+    {
+        return this._isMergingToVM;
+    },
+
+    checkMapping: function()
+    {
+        if (!this._script)
+            return;
+        if (typeof this._scriptSource !== "undefined")
+            return;
+        this._script.requestContent(callback.bind(this));
+
+        /**
+         * @param {?string} source
+         * @param {boolean} encoded
+         * @param {string} contentType
+         */
+        function callback(source, encoded, contentType)
+        {
+            this._scriptSource = source;
+            this._update();
+        }
+    },
+
+    dispose: function()
+    {
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
diff --git a/Source/devtools/front_end/ResourceTreeModel.js b/Source/devtools/front_end/ResourceTreeModel.js
new file mode 100644
index 0000000..a2c9bfd
--- /dev/null
+++ b/Source/devtools/front_end/ResourceTreeModel.js
@@ -0,0 +1,714 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.NetworkManager} networkManager
+ */
+WebInspector.ResourceTreeModel = function(networkManager)
+{
+    networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestFinished, this);
+    networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestUpdateDropped, this._onRequestUpdateDropped, this);
+
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._consoleMessageAdded, this);
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
+
+    PageAgent.enable();
+
+    NetworkAgent.enable();
+    this._fetchResourceTree();
+
+    InspectorBackend.registerPageDispatcher(new WebInspector.PageDispatcher(this));
+
+    this._pendingConsoleMessages = {};
+    this._securityOriginFrameCount = {};
+}
+
+WebInspector.ResourceTreeModel.EventTypes = {
+    FrameAdded: "FrameAdded",
+    FrameNavigated: "FrameNavigated",
+    FrameDetached: "FrameDetached",
+    MainFrameNavigated: "MainFrameNavigated",
+    MainFrameCreatedOrNavigated: "MainFrameCreatedOrNavigated",
+    ResourceAdded: "ResourceAdded",
+    WillLoadCachedResources: "WillLoadCachedResources",
+    CachedResourcesLoaded: "CachedResourcesLoaded",
+    DOMContentLoaded: "DOMContentLoaded",
+    OnLoad: "OnLoad",
+    InspectedURLChanged: "InspectedURLChanged",
+    SecurityOriginAdded: "SecurityOriginAdded",
+    SecurityOriginRemoved: "SecurityOriginRemoved"
+}
+
+WebInspector.ResourceTreeModel.prototype = {
+    _fetchResourceTree: function()
+    {
+        this._frames = {};
+        delete this._cachedResourcesProcessed;
+        PageAgent.getResourceTree(this._processCachedResources.bind(this));
+    },
+
+    _processCachedResources: function(error, mainFramePayload)
+    {
+        if (error) {
+            console.error(JSON.stringify(error));
+            return;
+        }
+
+        this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.WillLoadCachedResources);
+        WebInspector.inspectedPageURL = mainFramePayload.frame.url;
+        this._addFramesRecursively(null, mainFramePayload);
+        this._dispatchInspectedURLChanged();
+        this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded);
+        this._cachedResourcesProcessed = true;
+    },
+
+    cachedResourcesLoaded: function()
+    {
+        return this._cachedResourcesProcessed;
+    },
+
+    _dispatchInspectedURLChanged: function()
+    {
+        InspectorFrontendHost.inspectedURLChanged(WebInspector.inspectedPageURL);
+        this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, WebInspector.inspectedPageURL);
+    },
+
+    /**
+     * @param {WebInspector.ResourceTreeFrame} frame
+     * @param {boolean=} aboutToNavigate
+     */
+    _addFrame: function(frame, aboutToNavigate)
+    {
+        this._frames[frame.id] = frame;
+        if (frame.isMainFrame())
+            this.mainFrame = frame;
+        this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, frame);
+        if (!aboutToNavigate)
+            this._addSecurityOrigin(frame.securityOrigin);
+        if (frame.isMainFrame())
+            this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.MainFrameCreatedOrNavigated, frame);
+    },
+
+    /**
+     * @param {string} securityOrigin
+     */
+    _addSecurityOrigin: function(securityOrigin)
+    {
+        if (!this._securityOriginFrameCount[securityOrigin]) {
+            this._securityOriginFrameCount[securityOrigin] = 1;
+            this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginAdded, securityOrigin);
+            return;
+        }
+        this._securityOriginFrameCount[securityOrigin] += 1;
+    },
+
+    /**
+     * @param {string} securityOrigin
+     */
+    _removeSecurityOrigin: function(securityOrigin)
+    {
+        console.assert(this._securityOriginFrameCount[securityOrigin]);
+        if (this._securityOriginFrameCount[securityOrigin] === 1) {
+            delete this._securityOriginFrameCount[securityOrigin];
+            this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginRemoved, securityOrigin);
+            return;
+        }
+        this._securityOriginFrameCount[securityOrigin] -= 1;
+    },
+
+    /**
+     * @return {Array.<string>}
+     */
+    securityOrigins: function()
+    {
+        return Object.keys(this._securityOriginFrameCount);
+    },
+
+    /**
+     * @param {WebInspector.ResourceTreeFrame} mainFrame
+     */
+    _handleMainFrameDetached: function(mainFrame)
+    {
+        /**
+         * @param {WebInspector.ResourceTreeFrame} frame
+         */
+        function removeOriginForFrame(frame)
+        {
+            for (var i = 0; i < frame.childFrames.length; ++i)
+                removeOriginForFrame.call(this, frame.childFrames[i]);
+            if (!frame.isMainFrame())
+                this._removeSecurityOrigin(frame.securityOrigin);
+        }
+        removeOriginForFrame.call(this, WebInspector.resourceTreeModel.mainFrame);
+    },
+
+    /**
+     * @param {PageAgent.Frame} framePayload
+     */
+    _frameNavigated: function(framePayload)
+    {
+        // Do nothing unless cached resource tree is processed - it will overwrite everything.
+        if (!this._cachedResourcesProcessed)
+            return;
+        var frame = this._frames[framePayload.id];
+        var addedOrigin;
+        if (frame) {
+            // Navigation within existing frame.
+            this._removeSecurityOrigin(frame.securityOrigin);
+            frame._navigate(framePayload);
+            addedOrigin = frame.securityOrigin;
+        } else {
+            // Either a new frame or a main frame navigation to the new backend process. 
+            var parentFrame = this._frames[framePayload.parentId];
+            frame = new WebInspector.ResourceTreeFrame(this, parentFrame, framePayload);
+            if (frame.isMainFrame() && this.mainFrame) {
+                this._handleMainFrameDetached(this.mainFrame);
+                // Definitely a navigation to the new backend process.
+                this._frameDetached(this.mainFrame.id);
+            }
+            this._addFrame(frame, true);
+            addedOrigin = frame.securityOrigin;
+        }
+
+        if (frame.isMainFrame())
+            WebInspector.inspectedPageURL = frame.url;
+
+        this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, frame);
+        if (frame.isMainFrame()) {
+            this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, frame);
+            this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.MainFrameCreatedOrNavigated, frame);
+        }
+        if (addedOrigin)
+            this._addSecurityOrigin(addedOrigin);
+
+        // Fill frame with retained resources (the ones loaded using new loader).
+        var resources = frame.resources();
+        for (var i = 0; i < resources.length; ++i)
+            this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resources[i]);
+
+        if (frame.isMainFrame())
+            this._dispatchInspectedURLChanged();
+    },
+
+    /**
+     * @param {NetworkAgent.FrameId} frameId
+     */
+    _frameDetached: function(frameId)
+    {
+        // Do nothing unless cached resource tree is processed - it will overwrite everything.
+        if (!this._cachedResourcesProcessed)
+            return;
+
+        var frame = this._frames[frameId];
+        if (!frame)
+            return;
+
+        this._removeSecurityOrigin(frame.securityOrigin);
+        if (frame.parentFrame)
+            frame.parentFrame._removeChildFrame(frame);
+        else
+            frame._remove();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onRequestFinished: function(event)
+    {
+        if (!this._cachedResourcesProcessed)
+            return;
+
+        var request = /** @type {WebInspector.NetworkRequest} */ (event.data);
+        if (request.failed || request.type === WebInspector.resourceTypes.XHR)
+            return;
+
+        var frame = this._frames[request.frameId];
+        if (frame) {
+            var resource = frame._addRequest(request);
+            this._addPendingConsoleMessagesToResource(resource);
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onRequestUpdateDropped: function(event)
+    {
+        if (!this._cachedResourcesProcessed)
+            return;
+
+        var frameId = event.data.frameId;
+        var frame = this._frames[frameId];
+        if (!frame)
+            return;
+
+        var url = event.data.url;
+        if (frame._resourcesMap[url])
+            return;
+
+        var resource = new WebInspector.Resource(null, url, frame.url, frameId, event.data.loaderId, WebInspector.resourceTypes[event.data.resourceType], event.data.mimeType);
+        frame.addResource(resource);
+    },
+
+    /**
+     * @param {NetworkAgent.FrameId} frameId
+     * @return {WebInspector.ResourceTreeFrame}
+     */
+    frameForId: function(frameId)
+    {
+        return this._frames[frameId];
+    },
+
+    /**
+     * @param {function(WebInspector.Resource)} callback
+     * @return {boolean}
+     */
+    forAllResources: function(callback)
+    {
+        if (this.mainFrame)
+            return this.mainFrame._callForFrameResources(callback);
+        return false;
+    },
+
+    /**
+     * @return {Array.<WebInspector.ResourceTreeFrame>}
+     */
+    frames: function() 
+    {
+        return Object.values(this._frames);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _consoleMessageAdded: function(event)
+    {
+        var msg = /** @type {WebInspector.ConsoleMessage} */ (event.data);
+        var resource = msg.url ? this.resourceForURL(msg.url) : null;
+        if (resource)
+            this._addConsoleMessageToResource(msg, resource);
+        else
+            this._addPendingConsoleMessage(msg);
+    },
+
+    /**
+     * @param {WebInspector.ConsoleMessage} msg
+     */
+    _addPendingConsoleMessage: function(msg)
+    {
+        if (!msg.url)
+            return;
+        if (!this._pendingConsoleMessages[msg.url])
+            this._pendingConsoleMessages[msg.url] = [];
+        this._pendingConsoleMessages[msg.url].push(msg);
+    },
+
+    /**
+     * @param {WebInspector.Resource} resource
+     */
+    _addPendingConsoleMessagesToResource: function(resource)
+    {
+        var messages = this._pendingConsoleMessages[resource.url];
+        if (messages) {
+            for (var i = 0; i < messages.length; i++)
+                this._addConsoleMessageToResource(messages[i], resource);
+            delete this._pendingConsoleMessages[resource.url];
+        }
+    },
+
+    /**
+     * @param {WebInspector.ConsoleMessage} msg
+     * @param {WebInspector.Resource} resource
+     */
+    _addConsoleMessageToResource: function(msg, resource)
+    {
+        switch (msg.level) {
+        case WebInspector.ConsoleMessage.MessageLevel.Warning:
+            resource.warnings += msg.repeatDelta;
+            break;
+        case WebInspector.ConsoleMessage.MessageLevel.Error:
+            resource.errors += msg.repeatDelta;
+            break;
+        }
+        resource.addMessage(msg);
+    },
+
+    _consoleCleared: function()
+    {
+        function callback(resource)
+        {
+            resource.clearErrorsAndWarnings();
+        }
+
+        this._pendingConsoleMessages = {};
+        this.forAllResources(callback);
+    },
+
+    /**
+     * @param {string} url
+     * @return {WebInspector.Resource}
+     */
+    resourceForURL: function(url)
+    {
+        // Workers call into this with no frames available.
+        return this.mainFrame ? this.mainFrame.resourceForURL(url) : null;
+    },
+
+    /**
+     * @param {WebInspector.ResourceTreeFrame} parentFrame
+     * @param {PageAgent.FrameResourceTree} frameTreePayload
+     */
+    _addFramesRecursively: function(parentFrame, frameTreePayload)
+    {
+        var framePayload = frameTreePayload.frame;
+        var frame = new WebInspector.ResourceTreeFrame(this, parentFrame, framePayload);
+        this._addFrame(frame);
+
+        var frameResource = this._createResourceFromFramePayload(framePayload, framePayload.url, WebInspector.resourceTypes.Document, framePayload.mimeType);
+        if (frame.isMainFrame())
+            WebInspector.inspectedPageURL = frameResource.url;
+        frame.addResource(frameResource);
+
+        for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i)
+            this._addFramesRecursively(frame, frameTreePayload.childFrames[i]);
+
+        for (var i = 0; i < frameTreePayload.resources.length; ++i) {
+            var subresource = frameTreePayload.resources[i];
+            var resource = this._createResourceFromFramePayload(framePayload, subresource.url, WebInspector.resourceTypes[subresource.type], subresource.mimeType);
+            frame.addResource(resource);
+        }
+    },
+
+    /**
+     * @param {PageAgent.Frame} frame
+     * @param {string} url
+     * @param {WebInspector.ResourceType} type
+     * @param {string} mimeType
+     * @return {WebInspector.Resource}
+     */
+    _createResourceFromFramePayload: function(frame, url, type, mimeType)
+    {
+        return new WebInspector.Resource(null, url, frame.url, frame.id, frame.loaderId, type, mimeType);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.ResourceTreeModel} model
+ * @param {?WebInspector.ResourceTreeFrame} parentFrame
+ * @param {PageAgent.Frame} payload
+ */
+WebInspector.ResourceTreeFrame = function(model, parentFrame, payload)
+{
+    this._model = model;
+    this._parentFrame = parentFrame;
+
+    this._id = payload.id;
+    this._loaderId = payload.loaderId;
+    this._name = payload.name;
+    this._url = payload.url;
+    this._securityOrigin = payload.securityOrigin;
+    this._mimeType = payload.mimeType;
+
+    /**
+     * @type {Array.<WebInspector.ResourceTreeFrame>}
+     */
+    this._childFrames = [];
+
+    /**
+     * @type {Object.<string, WebInspector.Resource>}
+     */
+    this._resourcesMap = {};
+
+    if (this._parentFrame)
+        this._parentFrame._childFrames.push(this);
+}
+
+WebInspector.ResourceTreeFrame.prototype = {
+    /**
+     * @return {string}
+     */
+    get id()
+    {
+        return this._id;
+    },
+
+    /**
+     * @return {string}
+     */
+    get name()
+    {
+        return this._name || "";
+    },
+
+    /**
+     * @return {string}
+     */
+    get url()
+    {
+        return this._url;
+    },
+
+    /**
+     * @return {string}
+     */
+    get securityOrigin()
+    {
+        return this._securityOrigin;
+    },
+
+    /**
+     * @return {string}
+     */
+    get loaderId()
+    {
+        return this._loaderId;
+    },
+
+    /**
+     * @return {WebInspector.ResourceTreeFrame}
+     */
+    get parentFrame()
+    {
+        return this._parentFrame;
+    },
+
+    /**
+     * @return {Array.<WebInspector.ResourceTreeFrame>}
+     */
+    get childFrames()
+    {
+        return this._childFrames;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isMainFrame: function()
+    {
+        return !this._parentFrame;
+    },
+
+    /**
+     * @param {PageAgent.Frame} framePayload
+     */
+    _navigate: function(framePayload)
+    {
+        this._loaderId = framePayload.loaderId;
+        this._name = framePayload.name;
+        this._url = framePayload.url;
+        this._securityOrigin = framePayload.securityOrigin;
+        this._mimeType = framePayload.mimeType;
+
+        var mainResource = this._resourcesMap[this._url];
+        this._resourcesMap = {};
+        this._removeChildFrames();
+        if (mainResource && mainResource.loaderId === this._loaderId)
+            this.addResource(mainResource);
+    },
+
+    /**
+     * @return {WebInspector.Resource}
+     */
+    get mainResource()
+    {
+        return this._resourcesMap[this._url];
+    },
+
+    /**
+     * @param {WebInspector.ResourceTreeFrame} frame
+     */
+    _removeChildFrame: function(frame)
+    {
+        this._childFrames.remove(frame);
+        frame._remove();
+    },
+
+    _removeChildFrames: function()
+    {
+        var copy = this._childFrames.slice();
+        for (var i = 0; i < copy.length; ++i)
+            this._removeChildFrame(copy[i]); 
+    },
+
+    _remove: function()
+    {
+        this._removeChildFrames();
+        delete this._model._frames[this.id];
+        this._model.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this);
+    },
+
+    /**
+     * @param {WebInspector.Resource} resource
+     */
+    addResource: function(resource)
+    {
+        if (this._resourcesMap[resource.url] === resource) {
+            // Already in the tree, we just got an extra update.
+            return;
+        }
+        this._resourcesMap[resource.url] = resource;
+        this._model.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resource);
+    },
+
+    /**
+     * @param {WebInspector.NetworkRequest} request
+     * @return {WebInspector.Resource}
+     */
+    _addRequest: function(request)
+    {
+        var resource = this._resourcesMap[request.url];
+        if (resource && resource.request === request) {
+            // Already in the tree, we just got an extra update.
+            return resource;
+        }
+        resource = new WebInspector.Resource(request, request.url, request.documentURL, request.frameId, request.loaderId, request.type, request.mimeType);
+        this._resourcesMap[resource.url] = resource;
+        this._model.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resource);
+        return resource;
+    },
+
+    /**
+     * @return {Array.<WebInspector.Resource>}
+     */
+    resources: function()
+    {
+        var result = [];
+        for (var url in this._resourcesMap)
+            result.push(this._resourcesMap[url]);
+        return result;
+    },
+
+    /**
+     * @param {string} url
+     * @return {?WebInspector.Resource}
+     */
+    resourceForURL: function(url)
+    {
+        var result;
+        function filter(resource)
+        {
+            if (resource.url === url) {
+                result = resource;
+                return true;
+            }
+        }
+        this._callForFrameResources(filter);
+        return result;
+    },
+
+    /**
+     * @param {function(WebInspector.Resource)} callback
+     * @return {boolean}
+     */
+    _callForFrameResources: function(callback)
+    {
+        for (var url in this._resourcesMap) {
+            if (callback(this._resourcesMap[url]))
+                return true;
+        }
+
+        for (var i = 0; i < this._childFrames.length; ++i) {
+            if (this._childFrames[i]._callForFrameResources(callback))
+                return true;
+        }
+        return false;
+    }
+}
+
+/**
+ * @constructor
+ * @implements {PageAgent.Dispatcher}
+ */
+WebInspector.PageDispatcher = function(resourceTreeModel)
+{
+    this._resourceTreeModel = resourceTreeModel;
+}
+
+WebInspector.PageDispatcher.prototype = {
+    domContentEventFired: function(time)
+    {
+        this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, time);
+    },
+
+    loadEventFired: function(time)
+    {
+        this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.OnLoad, time);
+    },
+
+    frameNavigated: function(frame)
+    {
+        this._resourceTreeModel._frameNavigated(frame);
+    },
+
+    frameDetached: function(frameId)
+    {
+        this._resourceTreeModel._frameDetached(frameId);
+    },
+
+    frameStartedLoading: function(frameId)
+    {
+    },
+
+    frameStoppedLoading: function(frameId)
+    {
+    },
+
+    frameScheduledNavigation: function(frameId, delay)
+    {
+    },
+
+    frameClearedScheduledNavigation: function(frameId)
+    {
+    },
+
+    javascriptDialogOpening: function(message)
+    {
+    },
+
+    javascriptDialogClosed: function()
+    {
+    },
+
+    scriptsEnabled: function(isEnabled)
+    {
+        WebInspector.settings.javaScriptDisabled.set(!isEnabled);
+    }
+}
+
+/**
+ * @type {WebInspector.ResourceTreeModel}
+ */
+WebInspector.resourceTreeModel = null;
diff --git a/Source/devtools/front_end/ResourceType.js b/Source/devtools/front_end/ResourceType.js
new file mode 100644
index 0000000..a3f3110
--- /dev/null
+++ b/Source/devtools/front_end/ResourceType.js
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2012 Google Inc.  All rights reserved.
+ * Copyright (C) 2007, 2008 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @param {string} name
+ * @param {string} title
+ * @param {string} categoryTitle
+ * @param {string} color
+ * @param {boolean} isTextType
+ */
+WebInspector.ResourceType = function(name, title, categoryTitle, color, isTextType)
+{
+    this._name = name;
+    this._title = title;
+    this._categoryTitle = categoryTitle;
+    this._color = color;
+    this._isTextType = isTextType;
+}
+
+WebInspector.ResourceType.prototype = {
+    /**
+     * @return {string}
+     */
+    name: function()
+    {
+        return this._name;
+    },
+
+    /**
+     * @return {string}
+     */
+    title: function()
+    {
+        return this._title;
+    },
+
+    /**
+     * @return {string}
+     */
+    categoryTitle: function()
+    {
+        return this._categoryTitle;
+    },
+
+    /**
+     * @return {string}
+     */
+    color: function()
+    {
+        return this._color;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isTextType: function()
+    {
+        return this._isTextType;
+    },
+
+    /**
+     * @return {string}
+     */
+    toString: function()
+    {
+        return this._name;
+    },
+
+    /**
+     * @return {string}
+     */
+    canonicalMimeType: function()
+    {
+        if (this === WebInspector.resourceTypes.Document)
+            return "text/html";
+        if (this === WebInspector.resourceTypes.Script)
+            return "text/javascript";
+        if (this === WebInspector.resourceTypes.Stylesheet)
+            return "text/css";
+        return "";
+    }
+}
+
+/**
+ * Keep these in sync with WebCore::InspectorPageAgent::resourceTypeJson
+ * @enum {!WebInspector.ResourceType}
+ */
+WebInspector.resourceTypes = {
+    Document: new WebInspector.ResourceType("document", "Document", "Documents", "rgb(47,102,236)", true),
+    Stylesheet: new WebInspector.ResourceType("stylesheet", "Stylesheet", "Stylesheets", "rgb(157,231,119)", true),
+    Image: new WebInspector.ResourceType("image", "Image", "Images", "rgb(164,60,255)", false),
+    Script: new WebInspector.ResourceType("script", "Script", "Scripts", "rgb(255,121,0)", true),
+    XHR: new WebInspector.ResourceType("xhr", "XHR", "XHR", "rgb(231,231,10)", true),
+    Font: new WebInspector.ResourceType("font", "Font", "Fonts", "rgb(255,82,62)", false),
+    WebSocket: new WebInspector.ResourceType("websocket", "WebSocket", "WebSockets", "rgb(186,186,186)", false), // FIXME: Decide the color.
+    Other: new WebInspector.ResourceType("other", "Other", "Other", "rgb(186,186,186)", false)
+}
diff --git a/Source/devtools/front_end/ResourceUtils.js b/Source/devtools/front_end/ResourceUtils.js
new file mode 100644
index 0000000..5ef3fe8
--- /dev/null
+++ b/Source/devtools/front_end/ResourceUtils.js
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
+ * 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 Computer, 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.
+ */
+
+/**
+ * @param {string} url
+ * @return {?WebInspector.Resource}
+ */
+WebInspector.resourceForURL = function(url)
+{
+    return WebInspector.resourceTreeModel.resourceForURL(url);
+}
+
+/**
+ * @param {function(WebInspector.Resource)} callback
+ */
+WebInspector.forAllResources = function(callback)
+{
+     WebInspector.resourceTreeModel.forAllResources(callback);
+}
+
+/**
+ * @param {string} url
+ * @return {string}
+ */
+WebInspector.displayNameForURL = function(url)
+{
+    if (!url)
+        return "";
+
+    var resource = WebInspector.resourceForURL(url);
+    if (resource)
+        return resource.displayName;
+
+    var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(url);
+    if (uiSourceCode)
+        return uiSourceCode.displayName();
+
+    if (!WebInspector.inspectedPageURL)
+        return url.trimURL("");
+
+    var parsedURL = WebInspector.inspectedPageURL.asParsedURL();
+    var lastPathComponent = parsedURL ? parsedURL.lastPathComponent : parsedURL;
+    var index = WebInspector.inspectedPageURL.indexOf(lastPathComponent);
+    if (index !== -1 && index + lastPathComponent.length === WebInspector.inspectedPageURL.length) {
+        var baseURL = WebInspector.inspectedPageURL.substring(0, index);
+        if (url.startsWith(baseURL))
+            return url.substring(index);
+    }
+
+    if (!parsedURL)
+        return url;
+
+    var displayName = url.trimURL(parsedURL.host);
+    return displayName === "/" ? parsedURL.host + "/" : displayName;
+}
+
+/**
+ * @param {string} string
+ * @param {function(string,string,number=):Node} linkifier
+ * @return {DocumentFragment}
+ */
+WebInspector.linkifyStringAsFragmentWithCustomLinkifier = function(string, linkifier)
+{
+    var container = document.createDocumentFragment();
+    var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|data:|www\.)[\w$\-_+*'=\|\/\\(){}[\]^%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({^%@&#~]/;
+    var lineColumnRegEx = /:(\d+)(:(\d+))?$/;
+
+    while (string) {
+        var linkString = linkStringRegEx.exec(string);
+        if (!linkString)
+            break;
+
+        linkString = linkString[0];
+        var linkIndex = string.indexOf(linkString);
+        var nonLink = string.substring(0, linkIndex);
+        container.appendChild(document.createTextNode(nonLink));
+
+        var title = linkString;
+        var realURL = (linkString.startsWith("www.") ? "http://" + linkString : linkString);
+        var lineColumnMatch = lineColumnRegEx.exec(realURL);
+        var lineNumber;
+        if (lineColumnMatch) {
+            realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length);
+            lineNumber = parseInt(lineColumnMatch[1], 10);
+            lineNumber = isNaN(lineNumber) ? undefined : lineNumber;
+        }
+
+        var linkNode = linkifier(title, realURL, lineNumber);
+        container.appendChild(linkNode);
+        string = string.substring(linkIndex + linkString.length, string.length);
+    }
+
+    if (string)
+        container.appendChild(document.createTextNode(string));
+
+    return container;
+}
+
+/**
+ * @param {string} string
+ * @return {DocumentFragment}
+ */
+WebInspector.linkifyStringAsFragment = function(string)
+{
+    /**
+     * @param {string} title
+     * @param {string} url
+     * @param {number=} lineNumber
+     * @return {Node}
+     */
+    function linkifier(title, url, lineNumber)
+    {
+        var isExternal = !WebInspector.resourceForURL(url);
+        var urlNode = WebInspector.linkifyURLAsNode(url, title, undefined, isExternal);
+        if (typeof(lineNumber) !== "undefined") {
+            urlNode.lineNumber = lineNumber;
+            urlNode.preferredPanel = "scripts";
+        }
+        
+        return urlNode; 
+    }
+    
+    return WebInspector.linkifyStringAsFragmentWithCustomLinkifier(string, linkifier);
+}
+
+/**
+ * @param {string} url
+ * @param {string=} linkText
+ * @param {string=} classes
+ * @param {boolean=} isExternal
+ * @param {string=} tooltipText
+ * @return {!Element}
+ */
+WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal, tooltipText)
+{
+    if (!linkText)
+        linkText = url;
+    classes = (classes ? classes + " " : "");
+    classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link";
+
+    var a = document.createElement("a");
+    a.href = sanitizeHref(url);
+    a.className = classes;
+    if (typeof tooltipText === "undefined")
+        a.title = url;
+    else if (typeof tooltipText !== "string" || tooltipText.length)
+        a.title = tooltipText;
+    a.textContent = linkText.trimMiddle(WebInspector.Linkifier.MaxLengthForDisplayedURLs);
+    if (isExternal)
+        a.setAttribute("target", "_blank");
+
+    return a;
+}
+
+/**
+ * @param {string} url
+ * @param {number=} lineNumber
+ * @return {string}
+ */
+WebInspector.formatLinkText = function(url, lineNumber)
+{
+    var text = url ? WebInspector.displayNameForURL(url) : WebInspector.UIString("(program)");
+    if (typeof lineNumber === "number")
+        text += ":" + (lineNumber + 1);
+    return text;
+}
+
+/**
+ * @param {string} url
+ * @param {number=} lineNumber
+ * @param {string=} classes
+ * @param {string=} tooltipText
+ * @return {Element}
+ */
+WebInspector.linkifyResourceAsNode = function(url, lineNumber, classes, tooltipText)
+{
+    var linkText = WebInspector.formatLinkText(url, lineNumber);
+    var anchor = WebInspector.linkifyURLAsNode(url, linkText, classes, false, tooltipText);
+    anchor.lineNumber = lineNumber;
+    return anchor;
+}
+
+/**
+ * @param {WebInspector.NetworkRequest} request
+ * @param {string=} classes
+ * @return {Element}
+ */
+WebInspector.linkifyRequestAsNode = function(request, classes)
+{
+    var anchor = WebInspector.linkifyURLAsNode(request.url);
+    anchor.preferredPanel = "network";
+    anchor.requestId  = request.requestId;
+    return anchor;
+}
+
+/**
+ * @param {string} content
+ * @param {string} mimeType
+ * @param {boolean} contentEncoded
+ * @return {?string}
+ */
+WebInspector.contentAsDataURL = function(content, mimeType, contentEncoded)
+{
+    const maxDataUrlSize = 1024 * 1024;
+    if (content == null || content.length > maxDataUrlSize)
+        return null;
+
+    return "data:" + mimeType + (contentEncoded ? ";base64," : ",") + content;
+}
diff --git a/Source/devtools/front_end/ResourceView.js b/Source/devtools/front_end/ResourceView.js
new file mode 100644
index 0000000..4402d31
--- /dev/null
+++ b/Source/devtools/front_end/ResourceView.js
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) IBM Corp. 2009  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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @extends {WebInspector.View}
+ * @constructor
+ */
+WebInspector.ResourceView = function(resource)
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("resourceView.css");
+
+    this.element.addStyleClass("resource-view");
+    this.resource = resource;
+}
+
+WebInspector.ResourceView.prototype = {
+    hasContent: function()
+    {
+        return false;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @param {WebInspector.Resource} resource
+ */
+WebInspector.ResourceView.hasTextContent = function(resource)
+{
+    if (resource.type.isTextType())
+        return true; 
+    if (resource.type === WebInspector.resourceTypes.Other)
+        return resource.content && !resource.contentEncoded;
+    return false;
+}
+
+/**
+ * @param {WebInspector.Resource} resource
+ */
+WebInspector.ResourceView.nonSourceViewForResource = function(resource)
+{
+    switch (resource.type) {
+    case WebInspector.resourceTypes.Image:
+        return new WebInspector.ImageView(resource);
+    case WebInspector.resourceTypes.Font:
+        return new WebInspector.FontView(resource);
+    default:
+        return new WebInspector.ResourceView(resource);
+    }
+}
+
+/**
+ * @extends {WebInspector.SourceFrame}
+ * @constructor
+ * @param {WebInspector.Resource} resource
+ */
+WebInspector.ResourceSourceFrame = function(resource)
+{
+    this._resource = resource;
+    WebInspector.SourceFrame.call(this, resource);
+}
+
+WebInspector.ResourceSourceFrame.prototype = {
+    get resource()
+    {
+        return this._resource;
+    },
+
+    populateTextAreaContextMenu: function(contextMenu, lineNumber)
+    {
+        contextMenu.appendApplicableItems(this._resource);
+        if (this._resource.request)
+            contextMenu.appendApplicableItems(this._resource.request);
+    },
+
+    __proto__: WebInspector.SourceFrame.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.Resource} resource
+ */
+WebInspector.ResourceSourceFrameFallback = function(resource)
+{
+    WebInspector.View.call(this);
+    this._resource = resource;
+    this.element.addStyleClass("fill");
+    this.element.addStyleClass("script-view");
+    this._content = this.element.createChild("div", "script-view-fallback monospace");
+}
+
+WebInspector.ResourceSourceFrameFallback.prototype = {
+    wasShown: function()
+    {
+        if (!this._contentRequested) {
+            this._contentRequested = true;
+            this._resource.requestContent(this._contentLoaded.bind(this));
+        }
+    },
+
+ /**
+     * @param {?string} content
+     * @param {boolean} contentEncoded
+     * @param {string} mimeType
+     */
+    _contentLoaded: function(content, contentEncoded, mimeType)
+    {
+        this._content.textContent = content;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
\ No newline at end of file
diff --git a/Source/devtools/front_end/ResourceWebSocketFrameView.js b/Source/devtools/front_end/ResourceWebSocketFrameView.js
new file mode 100644
index 0000000..35cbb37
--- /dev/null
+++ b/Source/devtools/front_end/ResourceWebSocketFrameView.js
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.ResourceWebSocketFrameView = function(resource)
+{
+    WebInspector.View.call(this);
+    this.element.addStyleClass("resource-websocket");
+    this.resource = resource;
+    this.element.removeChildren();
+
+    var dataGrid = new WebInspector.DataGrid([
+        {id: "data", title: WebInspector.UIString("Data"), sortable: false},
+        {id: "length", title: WebInspector.UIString("Length"), sortable: false, alig: WebInspector.DataGrid.Align.Right, width: "50px"},
+        {id: "time", title: WebInspector.UIString("Time"), width: "70px"}
+    ]);
+
+    var frames = this.resource.frames();
+    for (var i = 0; i < frames.length; i++) {
+        var payload = frames[i];
+
+        var date = new Date(payload.time * 1000);
+        var row = {
+            data: "",
+            length: typeof payload.payloadData === "undefined" ? payload.errorMessage.length.toString() : payload.payloadData.length.toString(),
+            time: date.toLocaleTimeString()
+        };
+
+        var rowClass = "";
+        if (payload.errorMessage) {
+            rowClass = "error";
+            row.data = payload.errorMessage;
+        } else if (payload.opcode == WebInspector.ResourceWebSocketFrameView.OpCodes.TextFrame) {
+            if (payload.sent)
+                rowClass = "outcoming";
+
+            row.data = payload.payloadData;
+        } else {
+            rowClass = "opcode";
+            var opcodeMeaning = "";
+            switch (payload.opcode) {
+            case WebInspector.ResourceWebSocketFrameView.OpCodes.ContinuationFrame:
+                opcodeMeaning = WebInspector.UIString("Continuation Frame");
+                break;
+            case WebInspector.ResourceWebSocketFrameView.OpCodes.BinaryFrame:
+                opcodeMeaning = WebInspector.UIString("Binary Frame");
+                break;
+            case WebInspector.ResourceWebSocketFrameView.OpCodes.ConnectionCloseFrame:
+                opcodeMeaning = WebInspector.UIString("Connection Close Frame");
+                break;
+            case WebInspector.ResourceWebSocketFrameView.OpCodes.PingFrame:
+                opcodeMeaning = WebInspector.UIString("Ping Frame");
+                break;
+            case WebInspector.ResourceWebSocketFrameView.OpCodes.PongFrame:
+                opcodeMeaning = WebInspector.UIString("Pong Frame");
+                break;
+            }
+            row.data = WebInspector.UIString("%s (Opcode %d%s)", opcodeMeaning, payload.opcode, (payload.mask ? ", mask" : ""));
+        }
+
+        var node = new WebInspector.DataGridNode(row, false);
+        dataGrid.rootNode().appendChild(node);
+
+        if (rowClass)
+            node.element.classList.add("resource-websocket-row-" + rowClass);
+
+    }
+    dataGrid.show(this.element);
+}
+
+WebInspector.ResourceWebSocketFrameView.OpCodes = {
+    ContinuationFrame: 0,
+    TextFrame: 1,
+    BinaryFrame: 2,
+    ConnectionCloseFrame: 8,
+    PingFrame: 9,
+    PongFrame: 10
+};
+
+WebInspector.ResourceWebSocketFrameView.prototype = {
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/ResourcesPanel.js b/Source/devtools/front_end/ResourcesPanel.js
new file mode 100644
index 0000000..3013007
--- /dev/null
+++ b/Source/devtools/front_end/ResourcesPanel.js
@@ -0,0 +1,2314 @@
+/*
+ * Copyright (C) 2007, 2008, 2010 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Joseph Pecoraro
+ * Copyright (C) 2013 Samsung Electronics. 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+importScript("ApplicationCacheItemsView.js");
+importScript("DOMStorageItemsView.js");
+importScript("DatabaseQueryView.js");
+importScript("DatabaseTableView.js");
+importScript("DirectoryContentView.js");
+importScript("IndexedDBViews.js");
+importScript("FileContentView.js");
+importScript("FileSystemView.js");
+
+/**
+ * @constructor
+ * @extends {WebInspector.Panel}
+ */
+WebInspector.ResourcesPanel = function(database)
+{
+    WebInspector.Panel.call(this, "resources");
+    this.registerRequiredCSS("resourcesPanel.css");
+
+    WebInspector.settings.resourcesLastSelectedItem = WebInspector.settings.createSetting("resourcesLastSelectedItem", {});
+
+    this.createSidebarViewWithTree();
+    this.sidebarElement.addStyleClass("outline-disclosure");
+    this.sidebarElement.addStyleClass("filter-all");
+    this.sidebarElement.addStyleClass("children");
+    this.sidebarElement.addStyleClass("small");
+
+    this.sidebarTreeElement.removeStyleClass("sidebar-tree");
+
+    this.resourcesListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Frames"), "Frames", ["frame-storage-tree-item"]);
+    this.sidebarTree.appendChild(this.resourcesListTreeElement);
+
+    this.databasesListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Web SQL"), "Databases", ["database-storage-tree-item"]);
+    this.sidebarTree.appendChild(this.databasesListTreeElement);
+
+    this.indexedDBListTreeElement = new WebInspector.IndexedDBTreeElement(this);
+    this.sidebarTree.appendChild(this.indexedDBListTreeElement);
+
+    this.localStorageListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Local Storage"), "LocalStorage", ["domstorage-storage-tree-item", "local-storage"]);
+    this.sidebarTree.appendChild(this.localStorageListTreeElement);
+
+    this.sessionStorageListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Session Storage"), "SessionStorage", ["domstorage-storage-tree-item", "session-storage"]);
+    this.sidebarTree.appendChild(this.sessionStorageListTreeElement);
+
+    this.cookieListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Cookies"), "Cookies", ["cookie-storage-tree-item"]);
+    this.sidebarTree.appendChild(this.cookieListTreeElement);
+
+    this.applicationCacheListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Application Cache"), "ApplicationCache", ["application-cache-storage-tree-item"]);
+    this.sidebarTree.appendChild(this.applicationCacheListTreeElement);
+
+    if (WebInspector.experimentsSettings.fileSystemInspection.isEnabled()) {
+        this.fileSystemListTreeElement = new WebInspector.FileSystemListTreeElement(this);
+        this.sidebarTree.appendChild(this.fileSystemListTreeElement);
+    }
+
+    this.storageViews = this.splitView.mainElement;
+    this.storageViews.addStyleClass("diff-container");
+
+    this.storageViewStatusBarItemsContainer = document.createElement("div");
+    this.storageViewStatusBarItemsContainer.className = "status-bar-items";
+
+    this._databaseTableViews = new Map();
+    this._databaseQueryViews = new Map();
+    this._databaseTreeElements = new Map();
+    this._domStorageViews = new Map();
+    this._domStorageTreeElements = new Map();
+    this._cookieViews = {};
+    this._origins = {};
+    this._domains = {};
+
+    this.sidebarElement.addEventListener("mousemove", this._onmousemove.bind(this), false);
+    this.sidebarElement.addEventListener("mouseout", this._onmouseout.bind(this), false);
+
+    function viewGetter()
+    {
+        return this.visibleView;
+    }
+    WebInspector.GoToLineDialog.install(this, viewGetter.bind(this));
+
+    if (WebInspector.resourceTreeModel.cachedResourcesLoaded())
+        this._cachedResourcesLoaded();
+
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.OnLoad, this._onLoadEventFired, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded, this._cachedResourcesLoaded, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.WillLoadCachedResources, this._resetWithFrames, this);
+
+    WebInspector.databaseModel.databases().forEach(this._addDatabase.bind(this));
+    WebInspector.databaseModel.addEventListener(WebInspector.DatabaseModel.Events.DatabaseAdded, this._databaseAdded, this);
+}
+
+WebInspector.ResourcesPanel.prototype = {
+    get statusBarItems()
+    {
+        return [this.storageViewStatusBarItemsContainer];
+    },
+
+    wasShown: function()
+    {
+        WebInspector.Panel.prototype.wasShown.call(this);
+        this._initialize();
+    },
+
+    _initialize: function()
+    {
+        if (!this._initialized && this.isShowing() && this._cachedResourcesWereLoaded) {
+            this._populateResourceTree();
+            this._populateDOMStorageTree();
+            this._populateApplicationCacheTree();
+            this._initDefaultSelection();
+            this._initialized = true;
+        }
+    },
+
+    _onLoadEventFired: function()
+    {
+        this._initDefaultSelection();
+    },
+
+    _initDefaultSelection: function()
+    {
+        if (!this._initialized)
+            return;
+
+        var itemURL = WebInspector.settings.resourcesLastSelectedItem.get();
+        if (itemURL) {
+            for (var treeElement = this.sidebarTree.children[0]; treeElement; treeElement = treeElement.traverseNextTreeElement(false, this.sidebarTree, true)) {
+                if (treeElement.itemURL === itemURL) {
+                    treeElement.revealAndSelect(true);
+                    return;
+                }
+            }
+        }
+
+        var mainResource = WebInspector.inspectedPageURL && this.resourcesListTreeElement && this.resourcesListTreeElement.expanded && WebInspector.resourceTreeModel.resourceForURL(WebInspector.inspectedPageURL);
+        if (mainResource)
+            this.showResource(mainResource);
+    },
+
+    _resetWithFrames: function()
+    {
+        this.resourcesListTreeElement.removeChildren();
+        this._treeElementForFrameId = {};
+        this._reset();
+    },
+
+    _reset: function()
+    {
+        this._origins = {};
+        this._domains = {};
+        var queryViews = this._databaseQueryViews.values();
+        for (var i = 0; i < queryViews.length; ++i)
+            queryViews[i].removeEventListener(WebInspector.DatabaseQueryView.Events.SchemaUpdated, this._updateDatabaseTables, this);
+        this._databaseTableViews.clear();
+        this._databaseQueryViews.clear();
+        this._databaseTreeElements.clear();
+        this._domStorageViews.clear();
+        this._domStorageTreeElements.clear();
+        this._cookieViews = {};
+
+        this.databasesListTreeElement.removeChildren();
+        this.localStorageListTreeElement.removeChildren();
+        this.sessionStorageListTreeElement.removeChildren();
+        this.cookieListTreeElement.removeChildren();
+
+        if (this.visibleView && !(this.visibleView instanceof WebInspector.StorageCategoryView))
+            this.visibleView.detach();
+
+        this.storageViewStatusBarItemsContainer.removeChildren();
+
+        if (this.sidebarTree.selectedTreeElement)
+            this.sidebarTree.selectedTreeElement.deselect();
+    },
+
+    _populateResourceTree: function()
+    {
+        this._treeElementForFrameId = {};
+        WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, this._frameAdded, this);
+        WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, this._frameNavigated, this);
+        WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this._frameDetached, this);
+        WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this);
+
+        function populateFrame(frame)
+        {
+            this._frameAdded({data:frame});
+            for (var i = 0; i < frame.childFrames.length; ++i)
+                populateFrame.call(this, frame.childFrames[i]);
+
+            var resources = frame.resources();
+            for (var i = 0; i < resources.length; ++i)
+                this._resourceAdded({data:resources[i]});
+        }
+        populateFrame.call(this, WebInspector.resourceTreeModel.mainFrame);
+    },
+
+    _frameAdded: function(event)
+    {
+        var frame = event.data;
+        var parentFrame = frame.parentFrame;
+
+        var parentTreeElement = parentFrame ? this._treeElementForFrameId[parentFrame.id] : this.resourcesListTreeElement;
+        if (!parentTreeElement) {
+            console.warn("No frame to route " + frame.url + " to.")
+            return;
+        }
+
+        var frameTreeElement = new WebInspector.FrameTreeElement(this, frame);
+        this._treeElementForFrameId[frame.id] = frameTreeElement;
+        parentTreeElement.appendChild(frameTreeElement);
+    },
+
+    _frameDetached: function(event)
+    {
+        var frame = event.data;
+        var frameTreeElement = this._treeElementForFrameId[frame.id];
+        if (!frameTreeElement)
+            return;
+
+        delete this._treeElementForFrameId[frame.id];
+        if (frameTreeElement.parent)
+            frameTreeElement.parent.removeChild(frameTreeElement);
+    },
+
+    _resourceAdded: function(event)
+    {
+        var resource = event.data;
+        var frameId = resource.frameId;
+
+        if (resource.statusCode >= 301 && resource.statusCode <= 303)
+            return;
+
+        var frameTreeElement = this._treeElementForFrameId[frameId];
+        if (!frameTreeElement) {
+            // This is a frame's main resource, it will be retained
+            // and re-added by the resource manager;
+            return;
+        }
+
+        frameTreeElement.appendResource(resource);
+    },
+
+    _frameNavigated: function(event)
+    {
+        var frame = event.data;
+
+        if (!frame.parentFrame)
+            this._reset();
+
+        var frameId = frame.id;
+        var frameTreeElement = this._treeElementForFrameId[frameId];
+        if (frameTreeElement)
+            frameTreeElement.frameNavigated(frame);
+
+        var applicationCacheFrameTreeElement = this._applicationCacheFrameElements[frameId];
+        if (applicationCacheFrameTreeElement)
+            applicationCacheFrameTreeElement.frameNavigated(frame);
+    },
+
+    _cachedResourcesLoaded: function()
+    {
+        this._cachedResourcesWereLoaded = true;
+        this._initialize();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _databaseAdded: function(event)
+    {
+        var database = /** @type {WebInspector.Database} */ (event.data);
+        this._addDatabase(database);
+    },
+
+    /**
+     * @param {WebInspector.Database} database
+     */
+    _addDatabase: function(database)
+    {
+        var databaseTreeElement = new WebInspector.DatabaseTreeElement(this, database);
+        this._databaseTreeElements.put(database, databaseTreeElement);
+        this.databasesListTreeElement.appendChild(databaseTreeElement);
+    },
+
+    addDocumentURL: function(url)
+    {
+        var parsedURL = url.asParsedURL();
+        if (!parsedURL)
+            return;
+
+        var domain = parsedURL.host;
+        if (!this._domains[domain]) {
+            this._domains[domain] = true;
+
+            var cookieDomainTreeElement = new WebInspector.CookieTreeElement(this, domain);
+            this.cookieListTreeElement.appendChild(cookieDomainTreeElement);
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _domStorageAdded: function(event)
+    {
+        var domStorage = /** @type {WebInspector.DOMStorage} */ (event.data);
+        this._addDOMStorage(domStorage);
+    },
+
+    /**
+     * @param {WebInspector.DOMStorage} domStorage
+     */
+    _addDOMStorage: function(domStorage)
+    {
+        console.assert(!this._domStorageTreeElements.get(domStorage));
+
+        var domStorageTreeElement = new WebInspector.DOMStorageTreeElement(this, domStorage, (domStorage.isLocalStorage ? "local-storage" : "session-storage"));
+        this._domStorageTreeElements.put(domStorage, domStorageTreeElement);
+        if (domStorage.isLocalStorage)
+            this.localStorageListTreeElement.appendChild(domStorageTreeElement);
+        else
+            this.sessionStorageListTreeElement.appendChild(domStorageTreeElement);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _domStorageRemoved: function(event)
+    {
+        var domStorage = /** @type {WebInspector.DOMStorage} */ (event.data);
+        this._removeDOMStorage(domStorage);
+    },
+
+    /**
+     * @param {WebInspector.DOMStorage} domStorage
+     */
+    _removeDOMStorage: function(domStorage)
+    {
+        var treeElement = this._domStorageTreeElements.get(domStorage);
+        if (!treeElement)
+            return;
+        var wasSelected = treeElement.selected;
+        var parentListTreeElement = treeElement.parent;
+        parentListTreeElement.removeChild(treeElement);
+        if (wasSelected)
+            parentListTreeElement.select();
+        this._domStorageTreeElements.remove(treeElement);
+        this._domStorageViews.remove(domStorage);
+    },
+
+    /**
+     * @param {WebInspector.Database} database
+     */
+    selectDatabase: function(database)
+    {
+        if (database) {
+            this._showDatabase(database);
+            this._databaseTreeElements.get(database).select();
+        }
+    },
+
+    /**
+     * @param {WebInspector.DOMStorage} domStorage
+     */
+    selectDOMStorage: function(domStorage)
+    {
+        if (domStorage) {
+            this._showDOMStorage(domStorage);
+            this._domStorageTreeElements.get(domStorage).select();
+        }
+    },
+
+    canShowAnchorLocation: function(anchor)
+    {
+        return !!WebInspector.resourceForURL(anchor.href);
+    },
+
+    showAnchorLocation: function(anchor)
+    {
+        var resource = WebInspector.resourceForURL(anchor.href);
+        this.showResource(resource, anchor.lineNumber);
+    },
+
+    /**
+     * @param {number=} line
+     */
+    showResource: function(resource, line)
+    {
+        var resourceTreeElement = this._findTreeElementForResource(resource);
+        if (resourceTreeElement)
+            resourceTreeElement.revealAndSelect();
+
+        if (typeof line === "number") {
+            var view = this._resourceViewForResource(resource);
+            if (view.canHighlightLine())
+                view.highlightLine(line);
+        }
+        return true;
+    },
+
+    _showResourceView: function(resource)
+    {
+        var view = this._resourceViewForResource(resource);
+        if (!view) {
+            this.visibleView.detach();
+            return;
+        }
+        if (view.searchCanceled)
+            view.searchCanceled();
+        this._innerShowView(view);
+    },
+
+    _resourceViewForResource: function(resource)
+    {
+        if (WebInspector.ResourceView.hasTextContent(resource)) {
+            var treeElement = this._findTreeElementForResource(resource);
+            if (!treeElement)
+                return null;
+            return treeElement.sourceView();
+        }
+        return WebInspector.ResourceView.nonSourceViewForResource(resource);
+    },
+
+    /**
+     * @param {string=} tableName
+     */
+    _showDatabase: function(database, tableName)
+    {
+        if (!database)
+            return;
+
+        var view;
+        if (tableName) {
+            var tableViews = this._databaseTableViews.get(database);
+            if (!tableViews) {
+                tableViews = {};
+                this._databaseTableViews.put(database, tableViews);
+            }
+            view = tableViews[tableName];
+            if (!view) {
+                view = new WebInspector.DatabaseTableView(database, tableName);
+                tableViews[tableName] = view;
+            }
+        } else {
+            view = this._databaseQueryViews.get(database);
+            if (!view) {
+                view = new WebInspector.DatabaseQueryView(database);
+                this._databaseQueryViews.put(database, view);
+                view.addEventListener(WebInspector.DatabaseQueryView.Events.SchemaUpdated, this._updateDatabaseTables, this);
+            }
+        }
+
+        this._innerShowView(view);
+    },
+
+    /**
+     * @param {WebInspector.View} view
+     */
+    showIndexedDB: function(view)
+    {
+        this._innerShowView(view);
+    },
+
+    _showDOMStorage: function(domStorage)
+    {
+        if (!domStorage)
+            return;
+
+        var view;
+        view = this._domStorageViews.get(domStorage);
+        if (!view) {
+            view = new WebInspector.DOMStorageItemsView(domStorage, WebInspector.domStorageModel);
+            this._domStorageViews.put(domStorage, view);
+        }
+
+        this._innerShowView(view);
+    },
+
+    showCookies: function(treeElement, cookieDomain)
+    {
+        var view = this._cookieViews[cookieDomain];
+        if (!view) {
+            view = new WebInspector.CookieItemsView(treeElement, cookieDomain);
+            this._cookieViews[cookieDomain] = view;
+        }
+
+        this._innerShowView(view);
+    },
+
+    /**
+     * @param {string} cookieDomain
+     */
+    clearCookies: function(cookieDomain)
+    {
+        this._cookieViews[cookieDomain].clear();
+    },
+
+    showApplicationCache: function(frameId)
+    {
+        if (!this._applicationCacheViews[frameId])
+            this._applicationCacheViews[frameId] = new WebInspector.ApplicationCacheItemsView(this._applicationCacheModel, frameId);
+
+        this._innerShowView(this._applicationCacheViews[frameId]);
+    },
+
+    /**
+     *  @param {WebInspector.View} view
+     */
+    showFileSystem: function(view)
+    {
+        this._innerShowView(view);
+    },
+
+    showCategoryView: function(categoryName)
+    {
+        if (!this._categoryView)
+            this._categoryView = new WebInspector.StorageCategoryView();
+        this._categoryView.setText(categoryName);
+        this._innerShowView(this._categoryView);
+    },
+
+    _innerShowView: function(view)
+    {
+        if (this.visibleView === view)
+            return;
+
+        if (this.visibleView)
+            this.visibleView.detach();
+
+        view.show(this.storageViews);
+        this.visibleView = view;
+
+        this.storageViewStatusBarItemsContainer.removeChildren();
+        var statusBarItems = view.statusBarItems || [];
+        for (var i = 0; i < statusBarItems.length; ++i)
+            this.storageViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
+    },
+
+    closeVisibleView: function()
+    {
+        if (!this.visibleView)
+            return;
+        this.visibleView.detach();
+        delete this.visibleView;
+    },
+
+    _updateDatabaseTables: function(event)
+    {
+        var database = event.data;
+
+        if (!database)
+            return;
+
+        var databasesTreeElement = this._databaseTreeElements.get(database);
+        if (!databasesTreeElement)
+            return;
+
+        databasesTreeElement.shouldRefreshChildren = true;
+        var tableViews = this._databaseTableViews.get(database);
+
+        if (!tableViews)
+            return;
+
+        var tableNamesHash = {};
+        var self = this;
+        function tableNamesCallback(tableNames)
+        {
+            var tableNamesLength = tableNames.length;
+            for (var i = 0; i < tableNamesLength; ++i)
+                tableNamesHash[tableNames[i]] = true;
+
+            for (var tableName in tableViews) {
+                if (!(tableName in tableNamesHash)) {
+                    if (self.visibleView === tableViews[tableName])
+                        self.closeVisibleView();
+                    delete tableViews[tableName];
+                }
+            }
+        }
+        database.getTableNames(tableNamesCallback);
+    },
+
+    _populateDOMStorageTree: function()
+    {
+        WebInspector.domStorageModel.storages().forEach(this._addDOMStorage.bind(this));
+        WebInspector.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageAdded, this._domStorageAdded, this);
+        WebInspector.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageRemoved, this._domStorageRemoved, this);
+    },
+
+    _populateApplicationCacheTree: function()
+    {
+        this._applicationCacheModel = new WebInspector.ApplicationCacheModel();
+
+        this._applicationCacheViews = {};
+        this._applicationCacheFrameElements = {};
+        this._applicationCacheManifestElements = {};
+
+        this._applicationCacheModel.addEventListener(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestAdded, this._applicationCacheFrameManifestAdded, this);
+        this._applicationCacheModel.addEventListener(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestRemoved, this._applicationCacheFrameManifestRemoved, this);
+
+        this._applicationCacheModel.addEventListener(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestStatusUpdated, this._applicationCacheFrameManifestStatusChanged, this);
+        this._applicationCacheModel.addEventListener(WebInspector.ApplicationCacheModel.EventTypes.NetworkStateChanged, this._applicationCacheNetworkStateChanged, this);
+    },
+
+    _applicationCacheFrameManifestAdded: function(event)
+    {
+        var frameId = event.data;
+        var manifestURL = this._applicationCacheModel.frameManifestURL(frameId);
+        var status = this._applicationCacheModel.frameManifestStatus(frameId)
+
+        var manifestTreeElement = this._applicationCacheManifestElements[manifestURL]
+        if (!manifestTreeElement) {
+            manifestTreeElement = new WebInspector.ApplicationCacheManifestTreeElement(this, manifestURL);
+            this.applicationCacheListTreeElement.appendChild(manifestTreeElement);
+            this._applicationCacheManifestElements[manifestURL] = manifestTreeElement;
+        }
+
+        var frameTreeElement = new WebInspector.ApplicationCacheFrameTreeElement(this, frameId, manifestURL);
+        manifestTreeElement.appendChild(frameTreeElement);
+        manifestTreeElement.expand();
+        this._applicationCacheFrameElements[frameId] = frameTreeElement;
+    },
+
+    _applicationCacheFrameManifestRemoved: function(event)
+    {
+        var frameId = event.data;
+        var frameTreeElement = this._applicationCacheFrameElements[frameId];
+        if (!frameTreeElement)
+            return;
+
+        var manifestURL = frameTreeElement.manifestURL;
+        delete this._applicationCacheFrameElements[frameId];
+        delete this._applicationCacheViews[frameId];
+        frameTreeElement.parent.removeChild(frameTreeElement);
+
+        var manifestTreeElement = this._applicationCacheManifestElements[manifestURL];
+        if (manifestTreeElement.children.length !== 0)
+            return;
+
+        delete this._applicationCacheManifestElements[manifestURL];
+        manifestTreeElement.parent.removeChild(manifestTreeElement);
+    },
+
+    _applicationCacheFrameManifestStatusChanged: function(event)
+    {
+        var frameId = event.data;
+        var status = this._applicationCacheModel.frameManifestStatus(frameId)
+
+        if (this._applicationCacheViews[frameId])
+            this._applicationCacheViews[frameId].updateStatus(status);
+    },
+
+    _applicationCacheNetworkStateChanged: function(event)
+    {
+        var isNowOnline = event.data;
+
+        for (var manifestURL in this._applicationCacheViews)
+            this._applicationCacheViews[manifestURL].updateNetworkState(isNowOnline);
+    },
+
+    sidebarResized: function(event)
+    {
+        var width = event.data;
+        this.storageViewStatusBarItemsContainer.style.left = width + "px";
+    },
+
+    /**
+     * @param {string} query
+     */
+    performSearch: function(query)
+    {
+        this._resetSearchResults();
+        var regex = WebInspector.SourceFrame.createSearchRegex(query);
+        var totalMatchesCount = 0;
+
+        function callback(error, result)
+        {
+            if (!error) {
+                for (var i = 0; i < result.length; i++) {
+                    var searchResult = result[i];
+                    var frameTreeElement = this._treeElementForFrameId[searchResult.frameId];
+                    if (!frameTreeElement)
+                        continue;
+                    var resource = frameTreeElement.resourceByURL(searchResult.url);
+
+                    // FIXME: When the same script is used in several frames and this script contains at least
+                    // one search result then some search results can not be matched with a resource on panel.
+                    // https://bugs.webkit.org/show_bug.cgi?id=66005
+                    if (!resource)
+                        continue;
+
+                    this._findTreeElementForResource(resource).searchMatchesFound(searchResult.matchesCount);
+                    totalMatchesCount += searchResult.matchesCount;
+                }
+            }
+
+            WebInspector.searchController.updateSearchMatchesCount(totalMatchesCount, this);
+            this._searchController = new WebInspector.ResourcesSearchController(this.resourcesListTreeElement, totalMatchesCount);
+
+            if (this.sidebarTree.selectedTreeElement && this.sidebarTree.selectedTreeElement.searchMatchesCount)
+                this.jumpToNextSearchResult();
+        }
+
+        PageAgent.searchInResources(regex.source, !regex.ignoreCase, true, callback.bind(this));
+    },
+
+    _ensureViewSearchPerformed: function(callback)
+    {
+        function viewSearchPerformedCallback(searchId)
+        {
+            if (searchId !== this._lastViewSearchId)
+                return; // Search is obsolete.
+            this._viewSearchInProgress = false;
+            callback();
+        }
+
+        if (!this._viewSearchInProgress) {
+            if (!this.visibleView.hasSearchResults()) {
+                // We give id to each search, so that we can skip callbacks for obsolete searches.
+                this._lastViewSearchId = this._lastViewSearchId ? this._lastViewSearchId + 1 : 0;
+                this._viewSearchInProgress = true;
+                this.visibleView.performSearch(this.currentQuery, viewSearchPerformedCallback.bind(this, this._lastViewSearchId));
+            } else
+                callback();
+        }
+    },
+
+    _showSearchResult: function(searchResult)
+    {
+        this._lastSearchResultIndex = searchResult.index;
+        this._lastSearchResultTreeElement = searchResult.treeElement;
+
+        // At first show view for treeElement.
+        if (searchResult.treeElement !== this.sidebarTree.selectedTreeElement) {
+            this.showResource(searchResult.treeElement.representedObject);
+            WebInspector.searchController.showSearchField();
+        }
+
+        function callback(searchId)
+        {
+            if (this.sidebarTree.selectedTreeElement !== this._lastSearchResultTreeElement)
+                return; // User has selected another view while we were searching.
+            if (this._lastSearchResultIndex != -1)
+                this.visibleView.jumpToSearchResult(this._lastSearchResultIndex);
+            WebInspector.searchController.updateCurrentMatchIndex(searchResult.currentMatchIndex - 1, this);
+        }
+
+        // Then run SourceFrame search if needed and jump to search result index when done.
+        this._ensureViewSearchPerformed(callback.bind(this));
+    },
+
+    _resetSearchResults: function()
+    {
+        function callback(resourceTreeElement)
+        {
+            resourceTreeElement._resetSearchResults();
+        }
+
+        this._forAllResourceTreeElements(callback);
+        if (this.visibleView && this.visibleView.searchCanceled)
+            this.visibleView.searchCanceled();
+
+        this._lastSearchResultTreeElement = null;
+        this._lastSearchResultIndex = -1;
+        this._viewSearchInProgress = false;
+    },
+
+    searchCanceled: function()
+    {
+        function callback(resourceTreeElement)
+        {
+            resourceTreeElement._updateErrorsAndWarningsBubbles();
+        }
+
+        WebInspector.searchController.updateSearchMatchesCount(0, this);
+        this._resetSearchResults();
+        this._forAllResourceTreeElements(callback);
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        if (!this.currentSearchMatches)
+            return;
+        var currentTreeElement = this.sidebarTree.selectedTreeElement;
+        var nextSearchResult = this._searchController.nextSearchResult(currentTreeElement);
+        this._showSearchResult(nextSearchResult);
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        if (!this.currentSearchMatches)
+            return;
+        var currentTreeElement = this.sidebarTree.selectedTreeElement;
+        var previousSearchResult = this._searchController.previousSearchResult(currentTreeElement);
+        this._showSearchResult(previousSearchResult);
+    },
+
+    _forAllResourceTreeElements: function(callback)
+    {
+        var stop = false;
+        for (var treeElement = this.resourcesListTreeElement; !stop && treeElement; treeElement = treeElement.traverseNextTreeElement(false, this.resourcesListTreeElement, true)) {
+            if (treeElement instanceof WebInspector.FrameResourceTreeElement)
+                stop = callback(treeElement);
+        }
+    },
+
+    _findTreeElementForResource: function(resource)
+    {
+        function isAncestor(ancestor, object)
+        {
+            // Redirects, XHRs do not belong to the tree, it is fine to silently return false here.
+            return false;
+        }
+
+        function getParent(object)
+        {
+            // Redirects, XHRs do not belong to the tree, it is fine to silently return false here.
+            return null;
+        }
+
+        return this.sidebarTree.findTreeElement(resource, isAncestor, getParent);
+    },
+
+    showView: function(view)
+    {
+        if (view)
+            this.showResource(view.resource);
+    },
+
+    _onmousemove: function(event)
+    {
+        var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
+        if (!nodeUnderMouse)
+            return;
+
+        var listNode = nodeUnderMouse.enclosingNodeOrSelfWithNodeName("li");
+        if (!listNode)
+            return;
+
+        var element = listNode.treeElement;
+        if (this._previousHoveredElement === element)
+            return;
+
+        if (this._previousHoveredElement) {
+            this._previousHoveredElement.hovered = false;
+            delete this._previousHoveredElement;
+        }
+
+        if (element instanceof WebInspector.FrameTreeElement) {
+            this._previousHoveredElement = element;
+            element.hovered = true;
+        }
+    },
+
+    _onmouseout: function(event)
+    {
+        if (this._previousHoveredElement) {
+            this._previousHoveredElement.hovered = false;
+            delete this._previousHoveredElement;
+        }
+    },
+
+    __proto__: WebInspector.Panel.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {boolean=} hasChildren
+ * @param {boolean=} noIcon
+ */
+WebInspector.BaseStorageTreeElement = function(storagePanel, representedObject, title, iconClasses, hasChildren, noIcon)
+{
+    TreeElement.call(this, "", representedObject, hasChildren);
+    this._storagePanel = storagePanel;
+    this._titleText = title;
+    this._iconClasses = iconClasses;
+    this._noIcon = noIcon;
+}
+
+WebInspector.BaseStorageTreeElement.prototype = {
+    onattach: function()
+    {
+        this.listItemElement.removeChildren();
+        if (this._iconClasses) {
+            for (var i = 0; i < this._iconClasses.length; ++i)
+                this.listItemElement.addStyleClass(this._iconClasses[i]);
+        }
+
+        var selectionElement = document.createElement("div");
+        selectionElement.className = "selection";
+        this.listItemElement.appendChild(selectionElement);
+
+        if (!this._noIcon) {
+            this.imageElement = document.createElement("img");
+            this.imageElement.className = "icon";
+            this.listItemElement.appendChild(this.imageElement);
+        }
+
+        this.titleElement = document.createElement("div");
+        this.titleElement.className = "base-storage-tree-element-title";
+        this._titleTextNode = document.createTextNode("");
+        this.titleElement.appendChild(this._titleTextNode);
+        this._updateTitle();
+        this._updateSubtitle();
+        this.listItemElement.appendChild(this.titleElement);
+    },
+
+    get displayName()
+    {
+        return this._displayName;
+    },
+
+    _updateDisplayName: function()
+    {
+        this._displayName = this._titleText || "";
+        if (this._subtitleText)
+            this._displayName += " (" + this._subtitleText + ")";
+    },
+
+    _updateTitle: function()
+    {
+        this._updateDisplayName();
+
+        if (!this.titleElement)
+            return;
+
+        this._titleTextNode.textContent = this._titleText || "";
+    },
+
+    _updateSubtitle: function()
+    {
+        this._updateDisplayName();
+
+        if (!this.titleElement)
+            return;
+
+        if (this._subtitleText) {
+            if (!this._subtitleElement) {
+                this._subtitleElement = document.createElement("span");
+                this._subtitleElement.className = "base-storage-tree-element-subtitle";
+                this.titleElement.appendChild(this._subtitleElement);
+            }
+            this._subtitleElement.textContent = "(" + this._subtitleText + ")";
+        } else if (this._subtitleElement) {
+            this.titleElement.removeChild(this._subtitleElement);
+            delete this._subtitleElement;
+        }
+    },
+
+    onselect: function(selectedByUser)
+    {
+        if (!selectedByUser)
+            return;
+        var itemURL = this.itemURL;
+        if (itemURL)
+            WebInspector.settings.resourcesLastSelectedItem.set(itemURL);
+    },
+
+    onreveal: function()
+    {
+        if (this.listItemElement)
+            this.listItemElement.scrollIntoViewIfNeeded(false);
+    },
+
+    get titleText()
+    {
+        return this._titleText;
+    },
+
+    set titleText(titleText)
+    {
+        this._titleText = titleText;
+        this._updateTitle();
+    },
+
+    get subtitleText()
+    {
+        return this._subtitleText;
+    },
+
+    set subtitleText(subtitleText)
+    {
+        this._subtitleText = subtitleText;
+        this._updateSubtitle();
+    },
+
+    get searchMatchesCount()
+    {
+        return 0;
+    },
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ * @param {boolean=} noIcon
+ */
+WebInspector.StorageCategoryTreeElement = function(storagePanel, categoryName, settingsKey, iconClasses, noIcon)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, categoryName, iconClasses, true, noIcon);
+    this._expandedSettingKey = "resources" + settingsKey + "Expanded";
+    WebInspector.settings[this._expandedSettingKey] = WebInspector.settings.createSetting(this._expandedSettingKey, settingsKey === "Frames");
+    this._categoryName = categoryName;
+}
+
+WebInspector.StorageCategoryTreeElement.prototype = {
+    get itemURL()
+    {
+        return "category://" + this._categoryName;
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._storagePanel.showCategoryView(this._categoryName);
+    },
+
+    onattach: function()
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
+        if (WebInspector.settings[this._expandedSettingKey].get())
+            this.expand();
+    },
+
+    onexpand: function()
+    {
+        WebInspector.settings[this._expandedSettingKey].set(true);
+    },
+
+    oncollapse: function()
+    {
+        WebInspector.settings[this._expandedSettingKey].set(false);
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ */
+WebInspector.FrameTreeElement = function(storagePanel, frame)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, "", ["frame-storage-tree-item"]);
+    this._frame = frame;
+    this.frameNavigated(frame);
+}
+
+WebInspector.FrameTreeElement.prototype = {
+    frameNavigated: function(frame)
+    {
+        this.removeChildren();
+        this._frameId = frame.id;
+
+        this.titleText = frame.name;
+        this.subtitleText = new WebInspector.ParsedURL(frame.url).displayName;
+
+        this._categoryElements = {};
+        this._treeElementForResource = {};
+
+        this._storagePanel.addDocumentURL(frame.url);
+    },
+
+    get itemURL()
+    {
+        return "frame://" + encodeURI(this.displayName);
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._storagePanel.showCategoryView(this.displayName);
+
+        this.listItemElement.removeStyleClass("hovered");
+        DOMAgent.hideHighlight();
+    },
+
+    set hovered(hovered)
+    {
+        if (hovered) {
+            this.listItemElement.addStyleClass("hovered");
+            DOMAgent.highlightFrame(this._frameId, WebInspector.Color.PageHighlight.Content.toProtocolRGBA(), WebInspector.Color.PageHighlight.ContentOutline.toProtocolRGBA());
+        } else {
+            this.listItemElement.removeStyleClass("hovered");
+            DOMAgent.hideHighlight();
+        }
+    },
+
+    appendResource: function(resource)
+    {
+        if (resource.isHidden())
+            return;
+        var categoryName = resource.type.name();
+        var categoryElement = resource.type === WebInspector.resourceTypes.Document ? this : this._categoryElements[categoryName];
+        if (!categoryElement) {
+            categoryElement = new WebInspector.StorageCategoryTreeElement(this._storagePanel, resource.type.categoryTitle(), categoryName, null, true);
+            this._categoryElements[resource.type.name()] = categoryElement;
+            this._insertInPresentationOrder(this, categoryElement);
+        }
+        var resourceTreeElement = new WebInspector.FrameResourceTreeElement(this._storagePanel, resource);
+        this._insertInPresentationOrder(categoryElement, resourceTreeElement);
+        this._treeElementForResource[resource.url] = resourceTreeElement;
+    },
+
+    resourceByURL: function(url)
+    {
+        var treeElement = this._treeElementForResource[url];
+        return treeElement ? treeElement.representedObject : null;
+    },
+
+    appendChild: function(treeElement)
+    {
+        this._insertInPresentationOrder(this, treeElement);
+    },
+
+    _insertInPresentationOrder: function(parentTreeElement, childTreeElement)
+    {
+        // Insert in the alphabetical order, first frames, then resources. Document resource goes last.
+        function typeWeight(treeElement)
+        {
+            if (treeElement instanceof WebInspector.StorageCategoryTreeElement)
+                return 2;
+            if (treeElement instanceof WebInspector.FrameTreeElement)
+                return 1;
+            return 3;
+        }
+
+        function compare(treeElement1, treeElement2)
+        {
+            var typeWeight1 = typeWeight(treeElement1);
+            var typeWeight2 = typeWeight(treeElement2);
+
+            var result;
+            if (typeWeight1 > typeWeight2)
+                result = 1;
+            else if (typeWeight1 < typeWeight2)
+                result = -1;
+            else {
+                var title1 = treeElement1.displayName || treeElement1.titleText;
+                var title2 = treeElement2.displayName || treeElement2.titleText;
+                result = title1.localeCompare(title2);
+            }
+            return result;
+        }
+
+        var children = parentTreeElement.children;
+        var i;
+        for (i = 0; i < children.length; ++i) {
+            if (compare(childTreeElement, children[i]) < 0)
+                break;
+        }
+        parentTreeElement.insertChild(childTreeElement, i);
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ */
+WebInspector.FrameResourceTreeElement = function(storagePanel, resource)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, resource, resource.displayName, ["resource-sidebar-tree-item", "resources-type-" + resource.type.name()]);
+    this._resource = resource;
+    this._resource.addEventListener(WebInspector.Resource.Events.MessageAdded, this._consoleMessageAdded, this);
+    this._resource.addEventListener(WebInspector.Resource.Events.MessagesCleared, this._consoleMessagesCleared, this);
+    this.tooltip = resource.url;
+}
+
+WebInspector.FrameResourceTreeElement.prototype = {
+    get itemURL()
+    {
+        return this._resource.url;
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._storagePanel._showResourceView(this._resource);
+    },
+
+    ondblclick: function(event)
+    {
+        InspectorFrontendHost.openInNewTab(this._resource.url);
+    },
+
+    onattach: function()
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
+
+        if (this._resource.type === WebInspector.resourceTypes.Image) {
+            var previewImage = document.createElement("img");
+            previewImage.className = "image-resource-icon-preview";
+            this._resource.populateImageSource(previewImage);
+
+            var iconElement = document.createElement("div");
+            iconElement.className = "icon";
+            iconElement.appendChild(previewImage);
+            this.listItemElement.replaceChild(iconElement, this.imageElement);
+        }
+
+        this._statusElement = document.createElement("div");
+        this._statusElement.className = "status";
+        this.listItemElement.insertBefore(this._statusElement, this.titleElement);
+
+        this.listItemElement.draggable = true;
+        this.listItemElement.addEventListener("dragstart", this._ondragstart.bind(this), false);
+        this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+
+        this._updateErrorsAndWarningsBubbles();
+    },
+
+    _ondragstart: function(event)
+    {
+        event.dataTransfer.setData("text/plain", this._resource.content);
+        event.dataTransfer.effectAllowed = "copy";
+        return true;
+    },
+
+    _handleContextMenuEvent: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendApplicableItems(this._resource);
+        if (this._resource.request)
+            contextMenu.appendApplicableItems(this._resource.request);
+        contextMenu.show();
+    },
+
+    _setBubbleText: function(x)
+    {
+        if (!this._bubbleElement) {
+            this._bubbleElement = document.createElement("div");
+            this._bubbleElement.className = "bubble";
+            this._statusElement.appendChild(this._bubbleElement);
+        }
+
+        this._bubbleElement.textContent = x;
+    },
+
+    _resetBubble: function()
+    {
+        if (this._bubbleElement) {
+            this._bubbleElement.textContent = "";
+            this._bubbleElement.removeStyleClass("search-matches");
+            this._bubbleElement.removeStyleClass("warning");
+            this._bubbleElement.removeStyleClass("error");
+        }
+    },
+
+    _resetSearchResults: function()
+    {
+        this._resetBubble();
+        this._searchMatchesCount = 0;
+    },
+
+    get searchMatchesCount()
+    {
+        return this._searchMatchesCount;
+    },
+
+    searchMatchesFound: function(matchesCount)
+    {
+        this._resetSearchResults();
+
+        this._searchMatchesCount = matchesCount;
+        this._setBubbleText(matchesCount);
+        this._bubbleElement.addStyleClass("search-matches");
+
+        // Expand, do not scroll into view.
+        var currentAncestor = this.parent;
+        while (currentAncestor && !currentAncestor.root) {
+            if (!currentAncestor.expanded)
+                currentAncestor.expand();
+            currentAncestor = currentAncestor.parent;
+        }
+    },
+
+    _updateErrorsAndWarningsBubbles: function()
+    {
+        if (this._storagePanel.currentQuery)
+            return;
+
+        this._resetBubble();
+
+        if (this._resource.warnings || this._resource.errors)
+            this._setBubbleText(this._resource.warnings + this._resource.errors);
+
+        if (this._resource.warnings)
+            this._bubbleElement.addStyleClass("warning");
+
+        if (this._resource.errors)
+            this._bubbleElement.addStyleClass("error");
+    },
+
+    _consoleMessagesCleared: function()
+    {
+        // FIXME: move to the SourceFrame.
+        if (this._sourceView)
+            this._sourceView.clearMessages();
+
+        this._updateErrorsAndWarningsBubbles();
+    },
+
+    _consoleMessageAdded: function(event)
+    {
+        var msg = event.data;
+        if (this._sourceView)
+            this._sourceView.addMessage(msg);
+        this._updateErrorsAndWarningsBubbles();
+    },
+
+    sourceView: function()
+    {
+        if (!this._sourceView) {
+            this._sourceView = new WebInspector.ResourceSourceFrame(this._resource);
+            if (this._resource.messages) {
+                for (var i = 0; i < this._resource.messages.length; i++)
+                    this._sourceView.addMessage(this._resource.messages[i]);
+            }
+        }
+        return this._sourceView;
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ */
+WebInspector.DatabaseTreeElement = function(storagePanel, database)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, database.name, ["database-storage-tree-item"], true);
+    this._database = database;
+}
+
+WebInspector.DatabaseTreeElement.prototype = {
+    get itemURL()
+    {
+        return "database://" + encodeURI(this._database.name);
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._storagePanel._showDatabase(this._database);
+    },
+
+    onexpand: function()
+    {
+        this._updateChildren();
+    },
+
+    _updateChildren: function()
+    {
+        this.removeChildren();
+
+        function tableNamesCallback(tableNames)
+        {
+            var tableNamesLength = tableNames.length;
+            for (var i = 0; i < tableNamesLength; ++i)
+                this.appendChild(new WebInspector.DatabaseTableTreeElement(this._storagePanel, this._database, tableNames[i]));
+        }
+        this._database.getTableNames(tableNamesCallback.bind(this));
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ */
+WebInspector.DatabaseTableTreeElement = function(storagePanel, database, tableName)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, tableName, ["database-storage-tree-item"]);
+    this._database = database;
+    this._tableName = tableName;
+}
+
+WebInspector.DatabaseTableTreeElement.prototype = {
+    get itemURL()
+    {
+        return "database://" + encodeURI(this._database.name) + "/" + encodeURI(this._tableName);
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._storagePanel._showDatabase(this._database, this._tableName);
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StorageCategoryTreeElement}
+ * @param {WebInspector.ResourcesPanel} storagePanel
+ */
+WebInspector.IndexedDBTreeElement = function(storagePanel)
+{
+    WebInspector.StorageCategoryTreeElement.call(this, storagePanel, WebInspector.UIString("IndexedDB"), "IndexedDB", ["indexed-db-storage-tree-item"]);
+}
+
+WebInspector.IndexedDBTreeElement.prototype = {
+    onexpand: function()
+    {
+        WebInspector.StorageCategoryTreeElement.prototype.onexpand.call(this);
+        if (!this._indexedDBModel)
+            this._createIndexedDBModel();
+    },
+
+    onattach: function()
+    {
+        WebInspector.StorageCategoryTreeElement.prototype.onattach.call(this);
+        this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+    },
+
+    _handleContextMenuEvent: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString("Refresh IndexedDB"), this.refreshIndexedDB.bind(this));
+        contextMenu.show();
+    },
+
+    _createIndexedDBModel: function()
+    {
+        this._indexedDBModel = new WebInspector.IndexedDBModel();
+        this._idbDatabaseTreeElements = [];
+        this._indexedDBModel.addEventListener(WebInspector.IndexedDBModel.EventTypes.DatabaseAdded, this._indexedDBAdded, this);
+        this._indexedDBModel.addEventListener(WebInspector.IndexedDBModel.EventTypes.DatabaseRemoved, this._indexedDBRemoved, this);
+        this._indexedDBModel.addEventListener(WebInspector.IndexedDBModel.EventTypes.DatabaseLoaded, this._indexedDBLoaded, this);
+    },
+
+    refreshIndexedDB: function()
+    {
+        if (!this._indexedDBModel) {
+            this._createIndexedDBModel();
+            return;
+        }
+
+        this._indexedDBModel.refreshDatabaseNames();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _indexedDBAdded: function(event)
+    {
+        var databaseId = /** @type {WebInspector.IndexedDBModel.DatabaseId} */ (event.data);
+
+        var idbDatabaseTreeElement = new WebInspector.IDBDatabaseTreeElement(this._storagePanel, this._indexedDBModel, databaseId);
+        this._idbDatabaseTreeElements.push(idbDatabaseTreeElement);
+        this.appendChild(idbDatabaseTreeElement);
+
+        this._indexedDBModel.refreshDatabase(databaseId);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _indexedDBRemoved: function(event)
+    {
+        var databaseId = /** @type {WebInspector.IndexedDBModel.DatabaseId} */ (event.data);
+
+        var idbDatabaseTreeElement = this._idbDatabaseTreeElement(databaseId)
+        if (!idbDatabaseTreeElement)
+            return;
+
+        idbDatabaseTreeElement.clear();
+        this.removeChild(idbDatabaseTreeElement);
+        this._idbDatabaseTreeElements.remove(idbDatabaseTreeElement);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _indexedDBLoaded: function(event)
+    {
+        var database = /** @type {WebInspector.IndexedDBModel.Database} */ (event.data);
+
+        var idbDatabaseTreeElement = this._idbDatabaseTreeElement(database.databaseId)
+        if (!idbDatabaseTreeElement)
+            return;
+
+        idbDatabaseTreeElement.update(database);
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+     * @return {WebInspector.IDBDatabaseTreeElement}
+     */
+    _idbDatabaseTreeElement: function(databaseId)
+    {
+        var index = -1;
+        for (var i = 0; i < this._idbDatabaseTreeElements.length; ++i) {
+            if (this._idbDatabaseTreeElements[i]._databaseId.equals(databaseId)) {
+                index = i;
+                break;
+            }
+        }
+        if (index !== -1)
+            return this._idbDatabaseTreeElements[i];
+        return null;
+    },
+
+    __proto__: WebInspector.StorageCategoryTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StorageCategoryTreeElement}
+ * @param {WebInspector.ResourcesPanel} storagePanel
+ */
+WebInspector.FileSystemListTreeElement = function(storagePanel)
+{
+    WebInspector.StorageCategoryTreeElement.call(this, storagePanel, WebInspector.UIString("FileSystem"), "FileSystem", ["file-system-storage-tree-item"]);
+}
+
+WebInspector.FileSystemListTreeElement.prototype = {
+    onexpand: function()
+    {
+        WebInspector.StorageCategoryTreeElement.prototype.onexpand.call(this);
+        this._refreshFileSystem();
+    },
+
+    onattach: function()
+    {
+        WebInspector.StorageCategoryTreeElement.prototype.onattach.call(this);
+        this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+    },
+
+    _handleContextMenuEvent: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Refresh FileSystem list" : "Refresh FileSystem List"), this._refreshFileSystem.bind(this));
+        contextMenu.show();
+    },
+
+    _fileSystemAdded: function(event)
+    {
+        var fileSystem = /** @type {WebInspector.FileSystemModel.FileSystem} */ (event.data);
+        var fileSystemTreeElement = new WebInspector.FileSystemTreeElement(this._storagePanel, fileSystem);
+        this.appendChild(fileSystemTreeElement);
+    },
+
+    _fileSystemRemoved: function(event)
+    {
+        var fileSystem = /** @type {WebInspector.FileSystemModel.FileSystem} */ (event.data);
+        var fileSystemTreeElement = this._fileSystemTreeElementByName(fileSystem.name);
+        if (!fileSystemTreeElement)
+            return;
+        fileSystemTreeElement.clear();
+        this.removeChild(fileSystemTreeElement);
+    },
+
+    _fileSystemTreeElementByName: function(fileSystemName)
+    {
+        for (var i = 0; i < this.children.length; ++i) {
+            var child = /** @type {WebInspector.FileSystemTreeElement} */ (this.children[i]);
+            if (child.fileSystemName === fileSystemName)
+                return this.children[i];
+        }
+        return null;
+    },
+
+    _refreshFileSystem: function()
+    {
+        if (!this._fileSystemModel) {
+            this._fileSystemModel = new WebInspector.FileSystemModel();
+            this._fileSystemModel.addEventListener(WebInspector.FileSystemModel.EventTypes.FileSystemAdded, this._fileSystemAdded, this);
+            this._fileSystemModel.addEventListener(WebInspector.FileSystemModel.EventTypes.FileSystemRemoved, this._fileSystemRemoved, this);
+        }
+
+        this._fileSystemModel.refreshFileSystemList();
+    },
+
+    __proto__: WebInspector.StorageCategoryTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ * @param {WebInspector.ResourcesPanel} storagePanel
+ * @param {WebInspector.IndexedDBModel} model
+ * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+ */
+WebInspector.IDBDatabaseTreeElement = function(storagePanel, model, databaseId)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, databaseId.name + " - " + databaseId.securityOrigin, ["indexed-db-storage-tree-item"]);
+    this._model = model;
+    this._databaseId = databaseId;
+    this._idbObjectStoreTreeElements = {};
+}
+
+WebInspector.IDBDatabaseTreeElement.prototype = {
+    get itemURL()
+    {
+        return "indexedDB://" + this._databaseId.securityOrigin + "/" + this._databaseId.name;
+    },
+
+    onattach: function()
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
+        this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+    },
+
+    _handleContextMenuEvent: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString("Refresh IndexedDB"), this._refreshIndexedDB.bind(this));
+        contextMenu.show();
+    },
+
+    _refreshIndexedDB: function()
+    {
+        this._model.refreshDatabaseNames();
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.Database} database
+     */
+    update: function(database)
+    {
+        this._database = database;
+        var objectStoreNames = {};
+        for (var objectStoreName in this._database.objectStores) {
+            var objectStore = this._database.objectStores[objectStoreName];
+            objectStoreNames[objectStore.name] = true;
+            if (!this._idbObjectStoreTreeElements[objectStore.name]) {
+                var idbObjectStoreTreeElement = new WebInspector.IDBObjectStoreTreeElement(this._storagePanel, this._model, this._databaseId, objectStore);
+                this._idbObjectStoreTreeElements[objectStore.name] = idbObjectStoreTreeElement;
+                this.appendChild(idbObjectStoreTreeElement);
+            }
+            this._idbObjectStoreTreeElements[objectStore.name].update(objectStore);
+        }
+        for (var objectStoreName in this._idbObjectStoreTreeElements) {
+            if (!objectStoreNames[objectStoreName])
+                this._objectStoreRemoved(objectStoreName);
+        }
+
+        if (this.children.length) {
+            this.hasChildren = true;
+            this.expand();
+        }
+
+        if (this._view)
+            this._view.update(database);
+
+        this._updateTooltip();
+    },
+
+    _updateTooltip: function()
+    {
+        this.tooltip = WebInspector.UIString("Version") + ": " + this._database.version;
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        if (!this._view)
+            this._view = new WebInspector.IDBDatabaseView(this._database);
+
+        this._storagePanel.showIndexedDB(this._view);
+    },
+
+    /**
+     * @param {string} objectStoreName
+     */
+    _objectStoreRemoved: function(objectStoreName)
+    {
+        var objectStoreTreeElement = this._idbObjectStoreTreeElements[objectStoreName];
+        objectStoreTreeElement.clear();
+        this.removeChild(objectStoreTreeElement);
+        delete this._idbObjectStoreTreeElements[objectStoreName];
+    },
+
+    clear: function()
+    {
+        for (var objectStoreName in this._idbObjectStoreTreeElements)
+            this._objectStoreRemoved(objectStoreName);
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ * @param {WebInspector.ResourcesPanel} storagePanel
+ * @param {WebInspector.IndexedDBModel} model
+ * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+ * @param {WebInspector.IndexedDBModel.ObjectStore} objectStore
+ */
+WebInspector.IDBObjectStoreTreeElement = function(storagePanel, model, databaseId, objectStore)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, objectStore.name, ["indexed-db-object-store-storage-tree-item"]);
+    this._model = model;
+    this._databaseId = databaseId;
+    this._idbIndexTreeElements = {};
+}
+
+WebInspector.IDBObjectStoreTreeElement.prototype = {
+    get itemURL()
+    {
+        return "indexedDB://" + this._databaseId.securityOrigin + "/" + this._databaseId.name + "/" + this._objectStore.name;
+    },
+
+    onattach: function()
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
+        this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+    },
+
+    _handleContextMenuEvent: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString("Clear"), this._clearObjectStore.bind(this));
+        contextMenu.show();
+    },
+
+    _clearObjectStore: function()
+    {
+        function callback() {
+            this.update(this._objectStore);
+        }
+        this._model.clearObjectStore(this._databaseId, this._objectStore.name, callback.bind(this));
+    },
+
+   /**
+     * @param {WebInspector.IndexedDBModel.ObjectStore} objectStore
+     */
+    update: function(objectStore)
+    {
+        this._objectStore = objectStore;
+
+        var indexNames = {};
+        for (var indexName in this._objectStore.indexes) {
+            var index = this._objectStore.indexes[indexName];
+            indexNames[index.name] = true;
+            if (!this._idbIndexTreeElements[index.name]) {
+                var idbIndexTreeElement = new WebInspector.IDBIndexTreeElement(this._storagePanel, this._model, this._databaseId, this._objectStore, index);
+                this._idbIndexTreeElements[index.name] = idbIndexTreeElement;
+                this.appendChild(idbIndexTreeElement);
+            }
+            this._idbIndexTreeElements[index.name].update(index);
+        }
+        for (var indexName in this._idbIndexTreeElements) {
+            if (!indexNames[indexName])
+                this._indexRemoved(indexName);
+        }
+        for (var indexName in this._idbIndexTreeElements) {
+            if (!indexNames[indexName]) {
+                this.removeChild(this._idbIndexTreeElements[indexName]);
+                delete this._idbIndexTreeElements[indexName];
+            }
+        }
+
+        if (this.children.length) {
+            this.hasChildren = true;
+            this.expand();
+        }
+
+        if (this._view)
+            this._view.update(this._objectStore);
+
+        this._updateTooltip();
+    },
+
+    _updateTooltip: function()
+    {
+
+        var keyPathString = this._objectStore.keyPathString;
+        var tooltipString = keyPathString !== null ? (WebInspector.UIString("Key path: ") + keyPathString) : "";
+        if (this._objectStore.autoIncrement)
+            tooltipString += "\n" + WebInspector.UIString("autoIncrement");
+        this.tooltip = tooltipString
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        if (!this._view)
+            this._view = new WebInspector.IDBDataView(this._model, this._databaseId, this._objectStore, null);
+
+        this._storagePanel.showIndexedDB(this._view);
+    },
+
+    /**
+     * @param {string} indexName
+     */
+    _indexRemoved: function(indexName)
+    {
+        var indexTreeElement = this._idbIndexTreeElements[indexName];
+        indexTreeElement.clear();
+        this.removeChild(indexTreeElement);
+        delete this._idbIndexTreeElements[indexName];
+    },
+
+    clear: function()
+    {
+        for (var indexName in this._idbIndexTreeElements)
+            this._indexRemoved(indexName);
+        if (this._view)
+            this._view.clear();
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ * @param {WebInspector.ResourcesPanel} storagePanel
+ * @param {WebInspector.IndexedDBModel} model
+ * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
+ * @param {WebInspector.IndexedDBModel.ObjectStore} objectStore
+ * @param {WebInspector.IndexedDBModel.Index} index
+ */
+WebInspector.IDBIndexTreeElement = function(storagePanel, model, databaseId, objectStore, index)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, index.name, ["indexed-db-index-storage-tree-item"]);
+    this._model = model;
+    this._databaseId = databaseId;
+    this._objectStore = objectStore;
+    this._index = index;
+}
+
+WebInspector.IDBIndexTreeElement.prototype = {
+    get itemURL()
+    {
+        return "indexedDB://" + this._databaseId.securityOrigin + "/" + this._databaseId.name + "/" + this._objectStore.name + "/" + this._index.name;
+    },
+
+    /**
+     * @param {WebInspector.IndexedDBModel.Index} index
+     */
+    update: function(index)
+    {
+        this._index = index;
+
+        if (this._view)
+            this._view.update(this._index);
+
+        this._updateTooltip();
+    },
+
+    _updateTooltip: function()
+    {
+        var tooltipLines = [];
+        var keyPathString = this._index.keyPathString;
+        tooltipLines.push(WebInspector.UIString("Key path: ") + keyPathString);
+        if (this._index.unique)
+            tooltipLines.push(WebInspector.UIString("unique"));
+        if (this._index.multiEntry)
+            tooltipLines.push(WebInspector.UIString("multiEntry"));
+        this.tooltip = tooltipLines.join("\n");
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        if (!this._view)
+            this._view = new WebInspector.IDBDataView(this._model, this._databaseId, this._objectStore, this._index);
+
+        this._storagePanel.showIndexedDB(this._view);
+    },
+
+    clear: function()
+    {
+        if (this._view)
+            this._view.clear();
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ */
+WebInspector.DOMStorageTreeElement = function(storagePanel, domStorage, className)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, domStorage.securityOrigin ? domStorage.securityOrigin : WebInspector.UIString("Local Files"), ["domstorage-storage-tree-item", className]);
+    this._domStorage = domStorage;
+}
+
+WebInspector.DOMStorageTreeElement.prototype = {
+    get itemURL()
+    {
+        return "storage://" + this._domStorage.securityOrigin + "/" + (this._domStorage.isLocalStorage ? "local" : "session");
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._storagePanel._showDOMStorage(this._domStorage);
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ */
+WebInspector.CookieTreeElement = function(storagePanel, cookieDomain)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, cookieDomain ? cookieDomain : WebInspector.UIString("Local Files"), ["cookie-storage-tree-item"]);
+    this._cookieDomain = cookieDomain;
+}
+
+WebInspector.CookieTreeElement.prototype = {
+    get itemURL()
+    {
+        return "cookies://" + this._cookieDomain;
+    },
+
+    onattach: function()
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
+        this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _handleContextMenuEvent: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString("Clear"), this._clearCookies.bind(this));
+        contextMenu.show();
+    },
+
+    /**
+     * @param {string} domain
+     */
+    _clearCookies: function(domain)
+    {
+        this._storagePanel.clearCookies(this._cookieDomain);
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._storagePanel.showCookies(this, this._cookieDomain);
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ */
+WebInspector.ApplicationCacheManifestTreeElement = function(storagePanel, manifestURL)
+{
+    var title = new WebInspector.ParsedURL(manifestURL).displayName;
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, title, ["application-cache-storage-tree-item"]);
+    this.tooltip = manifestURL;
+    this._manifestURL = manifestURL;
+}
+
+WebInspector.ApplicationCacheManifestTreeElement.prototype = {
+    get itemURL()
+    {
+        return "appcache://" + this._manifestURL;
+    },
+
+    get manifestURL()
+    {
+        return this._manifestURL;
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._storagePanel.showCategoryView(this._manifestURL);
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ */
+WebInspector.ApplicationCacheFrameTreeElement = function(storagePanel, frameId, manifestURL)
+{
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, "", ["frame-storage-tree-item"]);
+    this._frameId = frameId;
+    this._manifestURL = manifestURL;
+    this._refreshTitles();
+}
+
+WebInspector.ApplicationCacheFrameTreeElement.prototype = {
+    get itemURL()
+    {
+        return "appcache://" + this._manifestURL + "/" + encodeURI(this.displayName);
+    },
+
+    get frameId()
+    {
+        return this._frameId;
+    },
+
+    get manifestURL()
+    {
+        return this._manifestURL;
+    },
+
+    _refreshTitles: function()
+    {
+        var frame = WebInspector.resourceTreeModel.frameForId(this._frameId);
+        if (!frame) {
+            this.subtitleText = WebInspector.UIString("new frame");
+            return;
+        }
+        this.titleText = frame.name;
+        this.subtitleText = new WebInspector.ParsedURL(frame.url).displayName;
+    },
+
+    frameNavigated: function()
+    {
+        this._refreshTitles();
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._storagePanel.showApplicationCache(this._frameId);
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.BaseStorageTreeElement}
+ * @param {WebInspector.ResourcesPanel} storagePanel
+ * @param {WebInspector.FileSystemModel.FileSystem} fileSystem
+ */
+WebInspector.FileSystemTreeElement = function(storagePanel, fileSystem)
+{
+    var displayName = fileSystem.type + " - " + fileSystem.origin;
+    WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, displayName, ["file-system-storage-tree-item"]);
+    this._fileSystem = fileSystem;
+}
+
+WebInspector.FileSystemTreeElement.prototype = {
+    get fileSystemName()
+    {
+        return this._fileSystem.name;
+    },
+
+    get itemURL()
+    {
+        return "filesystem://" + this._fileSystem.name;
+    },
+
+    onselect: function(selectedByUser)
+    {
+        WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
+        this._fileSystemView = new WebInspector.FileSystemView(this._fileSystem);
+        this._storagePanel.showFileSystem(this._fileSystemView);
+    },
+
+    clear: function()
+    {
+        if (this.fileSystemView && this._storagePanel.visibleView === this.fileSystemView)
+            this._storagePanel.closeVisibleView();
+    },
+
+    __proto__: WebInspector.BaseStorageTreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.StorageCategoryView = function()
+{
+    WebInspector.View.call(this);
+
+    this.element.addStyleClass("storage-view");
+    this._emptyView = new WebInspector.EmptyView("");
+    this._emptyView.show(this.element);
+}
+
+WebInspector.StorageCategoryView.prototype = {
+    setText: function(text)
+    {
+        this._emptyView.text = text;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.BaseStorageTreeElement} rootElement
+ * @param {number} matchesCount
+ */
+WebInspector.ResourcesSearchController = function(rootElement, matchesCount)
+{
+    this._root = rootElement;
+    this._matchesCount = matchesCount;
+    this._traverser = new WebInspector.SearchResultsTreeElementsTraverser(rootElement);
+    this._lastTreeElement = null;
+    this._lastIndex = -1;
+}
+
+WebInspector.ResourcesSearchController.prototype = {
+    /**
+     * @param {WebInspector.BaseStorageTreeElement} currentTreeElement
+     */
+    nextSearchResult: function(currentTreeElement)
+    {
+        if (!currentTreeElement)
+            return this._searchResult(this._traverser.first(), 0, 1);
+
+        if (!currentTreeElement.searchMatchesCount)
+            return this._searchResult(this._traverser.next(currentTreeElement), 0);
+
+        if (this._lastTreeElement !== currentTreeElement || this._lastIndex === -1)
+            return this._searchResult(currentTreeElement, 0);
+
+        if (this._lastIndex === currentTreeElement.searchMatchesCount - 1)
+            return this._searchResult(this._traverser.next(currentTreeElement), 0, this._currentMatchIndex % this._matchesCount + 1);
+
+        return this._searchResult(currentTreeElement, this._lastIndex + 1, this._currentMatchIndex + 1);
+    },
+
+    /**
+     * @param {WebInspector.BaseStorageTreeElement} currentTreeElement
+     */
+    previousSearchResult: function(currentTreeElement)
+    {
+        if (!currentTreeElement) {
+            var treeElement = this._traverser.last();
+            return this._searchResult(treeElement, treeElement.searchMatchesCount - 1, this._matchesCount);
+        }
+
+        if (currentTreeElement.searchMatchesCount && this._lastTreeElement === currentTreeElement) {
+            if (this._lastIndex > 0)
+                return this._searchResult(currentTreeElement, this._lastIndex - 1, this._currentMatchIndex - 1);
+            else {
+                var treeElement = this._traverser.previous(currentTreeElement);
+                var currentMatchIndex = this._currentMatchIndex - 1 ? this._currentMatchIndex - 1 : this._matchesCount;
+                return this._searchResult(treeElement, treeElement.searchMatchesCount - 1, currentMatchIndex);
+            }
+        }
+
+        var treeElement = this._traverser.previous(currentTreeElement)
+        return this._searchResult(treeElement, treeElement.searchMatchesCount - 1);
+    },
+
+    /**
+     * @param {WebInspector.BaseStorageTreeElement} treeElement
+     * @param {number} index
+     * @param {number=} currentMatchIndex
+     * @return {Object}
+     */
+    _searchResult: function(treeElement, index, currentMatchIndex)
+    {
+        this._lastTreeElement = treeElement;
+        this._lastIndex = index;
+        if (!currentMatchIndex)
+            currentMatchIndex = this._traverser.matchIndex(treeElement, index);
+        this._currentMatchIndex = currentMatchIndex;
+        return {treeElement: treeElement, index: index, currentMatchIndex: currentMatchIndex};
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.BaseStorageTreeElement} rootElement
+ */
+WebInspector.SearchResultsTreeElementsTraverser = function(rootElement)
+{
+    this._root = rootElement;
+}
+
+WebInspector.SearchResultsTreeElementsTraverser.prototype = {
+    /**
+     * @return {WebInspector.BaseStorageTreeElement}
+     */
+    first: function()
+    {
+        return this.next(this._root);
+    },
+
+    /**
+     * @return {WebInspector.BaseStorageTreeElement}
+     */
+    last: function()
+    {
+        return this.previous(this._root);
+    },
+
+    /**
+     * @param {WebInspector.BaseStorageTreeElement} startTreeElement
+     * @return {WebInspector.BaseStorageTreeElement}
+     */
+    next: function(startTreeElement)
+    {
+        var treeElement = startTreeElement;
+        do {
+            treeElement = this._traverseNext(treeElement) || this._root;
+        } while (treeElement != startTreeElement && !this._elementSearchMatchesCount(treeElement));
+        return treeElement;
+    },
+
+    /**
+     * @param {WebInspector.BaseStorageTreeElement} startTreeElement
+     * @return {WebInspector.BaseStorageTreeElement}
+     */
+    previous: function(startTreeElement)
+    {
+        var treeElement = startTreeElement;
+        do {
+            treeElement = this._traversePrevious(treeElement) || this._lastTreeElement();
+        } while (treeElement != startTreeElement && !this._elementSearchMatchesCount(treeElement));
+        return treeElement;
+    },
+
+    /**
+     * @param {WebInspector.BaseStorageTreeElement} startTreeElement
+     * @param {number} index
+     * @return {number}
+     */
+    matchIndex: function(startTreeElement, index)
+    {
+        var matchIndex = 1;
+        var treeElement = this._root;
+        while (treeElement != startTreeElement) {
+            matchIndex += this._elementSearchMatchesCount(treeElement);
+            treeElement = this._traverseNext(treeElement) || this._root;
+            if (treeElement === this._root)
+                return 0;
+        }
+        return matchIndex + index;
+    },
+
+    /**
+     * @param {WebInspector.BaseStorageTreeElement} treeElement
+     * @return {number}
+     */
+    _elementSearchMatchesCount: function(treeElement)
+    {
+        return treeElement.searchMatchesCount;
+    },
+
+    /**
+     * @param {WebInspector.BaseStorageTreeElement} treeElement
+     * @return {WebInspector.BaseStorageTreeElement}
+     */
+    _traverseNext: function(treeElement)
+    {
+        return /** @type {WebInspector.BaseStorageTreeElement} */ (treeElement.traverseNextTreeElement(false, this._root, true));
+    },
+
+    /**
+     * @param {WebInspector.BaseStorageTreeElement} treeElement
+     * @return {WebInspector.BaseStorageTreeElement}
+     */
+    _traversePrevious: function(treeElement)
+    {
+        return /** @type {WebInspector.BaseStorageTreeElement} */ (treeElement.traversePreviousTreeElement(false, true));
+    },
+
+    /**
+     * @return {WebInspector.BaseStorageTreeElement}
+     */
+    _lastTreeElement: function()
+    {
+        var treeElement = this._root;
+        var nextTreeElement;
+        while (nextTreeElement = this._traverseNext(treeElement))
+            treeElement = nextTreeElement;
+        return treeElement;
+    }
+}
diff --git a/Source/devtools/front_end/RevisionHistoryView.js b/Source/devtools/front_end/RevisionHistoryView.js
new file mode 100644
index 0000000..9134833
--- /dev/null
+++ b/Source/devtools/front_end/RevisionHistoryView.js
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.RevisionHistoryView = function()
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("revisionHistory.css");
+    this.element.addStyleClass("revision-history-drawer");
+    this.element.addStyleClass("fill");
+    this.element.addStyleClass("outline-disclosure");
+    this._uiSourceCodeItems = new Map();
+
+    var olElement = this.element.createChild("ol");
+    this._treeOutline = new TreeOutline(olElement);
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    function populateRevisions(uiSourceCode)
+    {
+        if (uiSourceCode.history.length)
+            this._createUISourceCodeItem(uiSourceCode);
+    }
+
+    WebInspector.workspace.uiSourceCodes().forEach(populateRevisions.bind(this));
+    WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeContentCommitted, this._revisionAdded, this);
+    WebInspector.workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
+    WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset, this);
+
+    this._statusElement = document.createElement("span");
+    this._statusElement.textContent = WebInspector.UIString("Local modifications");
+
+}
+
+/**
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ */
+WebInspector.RevisionHistoryView.showHistory = function(uiSourceCode)
+{
+    if (!WebInspector.RevisionHistoryView._view) 
+        WebInspector.RevisionHistoryView._view = new WebInspector.RevisionHistoryView();
+    var view = WebInspector.RevisionHistoryView._view;
+    WebInspector.showViewInDrawer(view._statusElement, view);
+    view._revealUISourceCode(uiSourceCode);
+}
+
+WebInspector.RevisionHistoryView.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _createUISourceCodeItem: function(uiSourceCode)
+    {
+        var uiSourceCodeItem = new TreeElement(uiSourceCode.displayName(), null, true);
+        uiSourceCodeItem.selectable = false;
+
+        // Insert in sorted order
+        for (var i = 0; i < this._treeOutline.children.length; ++i) {
+            if (this._treeOutline.children[i].title.localeCompare(uiSourceCode.displayName()) > 0) {
+                this._treeOutline.insertChild(uiSourceCodeItem, i);
+                break;
+            }
+        }
+        if (i === this._treeOutline.children.length)
+            this._treeOutline.appendChild(uiSourceCodeItem);
+
+        this._uiSourceCodeItems.put(uiSourceCode, uiSourceCodeItem);
+
+        var revisionCount = uiSourceCode.history.length;
+        for (var i = revisionCount - 1; i >= 0; --i) {
+            var revision = uiSourceCode.history[i];
+            var historyItem = new WebInspector.RevisionHistoryTreeElement(revision, uiSourceCode.history[i - 1], i !== revisionCount - 1);
+            uiSourceCodeItem.appendChild(historyItem);
+        }
+
+        var linkItem = new TreeElement("", null, false);
+        linkItem.selectable = false;
+        uiSourceCodeItem.appendChild(linkItem);
+
+        var revertToOriginal = linkItem.listItemElement.createChild("span", "revision-history-link revision-history-link-row");
+        revertToOriginal.textContent = WebInspector.UIString("apply original content");
+        revertToOriginal.addEventListener("click", uiSourceCode.revertToOriginal.bind(uiSourceCode));
+
+        var clearHistoryElement = uiSourceCodeItem.listItemElement.createChild("span", "revision-history-link");
+        clearHistoryElement.textContent = WebInspector.UIString("revert");
+        clearHistoryElement.addEventListener("click", this._clearHistory.bind(this, uiSourceCode));
+        return uiSourceCodeItem;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _clearHistory: function(uiSourceCode)
+    {
+        uiSourceCode.revertAndClearHistory(this._removeUISourceCode.bind(this));
+    },
+
+    _revisionAdded: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode);
+        var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode);
+        if (!uiSourceCodeItem) {
+            uiSourceCodeItem = this._createUISourceCodeItem(uiSourceCode);
+            return;
+        }
+
+        var historyLength = uiSourceCode.history.length;
+        var historyItem = new WebInspector.RevisionHistoryTreeElement(uiSourceCode.history[historyLength - 1], uiSourceCode.history[historyLength - 2], false);
+        if (uiSourceCodeItem.children.length)
+            uiSourceCodeItem.children[0].allowRevert();
+        uiSourceCodeItem.insertChild(historyItem, 0);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _revealUISourceCode: function(uiSourceCode)
+    {
+        var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode);
+        if (uiSourceCodeItem) {
+            uiSourceCodeItem.reveal();
+            uiSourceCodeItem.expand();
+        }
+    },
+
+    _uiSourceCodeRemoved: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        this._removeUISourceCode(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _removeUISourceCode: function(uiSourceCode)
+    {
+        var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode);
+        if (!uiSourceCodeItem)
+            return;
+        this._treeOutline.removeChild(uiSourceCodeItem);
+        this._uiSourceCodeItems.remove(uiSourceCode);
+    },
+
+    _projectWillReset: function(event)
+    {
+        var project = event.data;
+        project.uiSourceCodes().forEach(this._removeUISourceCode.bind(this));
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {WebInspector.Revision} revision
+ * @param {WebInspector.Revision} baseRevision
+ * @param {boolean} allowRevert
+ */
+WebInspector.RevisionHistoryTreeElement = function(revision, baseRevision, allowRevert)
+{
+    TreeElement.call(this, revision.timestamp.toLocaleTimeString(), null, true);
+    this.selectable = false;
+
+    this._revision = revision;
+    this._baseRevision = baseRevision;
+
+    this._revertElement = document.createElement("span");
+    this._revertElement.className = "revision-history-link";
+    this._revertElement.textContent = WebInspector.UIString("apply revision content");
+    this._revertElement.addEventListener("click", this._revision.revertToThis.bind(this._revision), false);
+    if (!allowRevert)
+        this._revertElement.addStyleClass("hidden");
+}
+
+WebInspector.RevisionHistoryTreeElement.prototype = {
+    onattach: function()
+    {
+        this.listItemElement.addStyleClass("revision-history-revision");
+    },
+
+    onexpand: function()
+    {
+        this.listItemElement.appendChild(this._revertElement);
+
+        if (this._wasExpandedOnce)
+            return;
+        this._wasExpandedOnce = true;
+
+        this.childrenListElement.addStyleClass("source-code");
+        if (this._baseRevision)
+            this._baseRevision.requestContent(step1.bind(this));
+        else
+            this._revision.uiSourceCode.requestOriginalContent(step1.bind(this));
+
+        function step1(baseContent)
+        {
+            this._revision.requestContent(step2.bind(this, baseContent));
+        }
+
+        function step2(baseContent, newContent)
+        {
+            var baseLines = difflib.stringAsLines(baseContent);
+            var newLines = difflib.stringAsLines(newContent);
+            var sm = new difflib.SequenceMatcher(baseLines, newLines);
+            var opcodes = sm.get_opcodes();
+            var lastWasSeparator = false;
+
+            for (var idx = 0; idx < opcodes.length; idx++) {
+                var code = opcodes[idx];
+                var change = code[0];
+                var b = code[1];
+                var be = code[2];
+                var n = code[3];
+                var ne = code[4];
+                var rowCount = Math.max(be - b, ne - n);
+                var topRows = [];
+                var bottomRows = [];
+                for (var i = 0; i < rowCount; i++) {
+                    if (change === "delete" || (change === "replace" && b < be)) {
+                        var lineNumber = b++;
+                        this._createLine(lineNumber, null, baseLines[lineNumber], "removed");
+                        lastWasSeparator = false;
+                    }
+
+                    if (change === "insert" || (change === "replace" && n < ne)) {
+                        var lineNumber = n++;
+                        this._createLine(null, lineNumber, newLines[lineNumber], "added");
+                        lastWasSeparator = false;
+                    }
+
+                    if (change === "equal") {
+                        b++;
+                        n++;
+                        if (!lastWasSeparator)
+                            this._createLine(null, null, "    \u2026", "separator");
+                        lastWasSeparator = true;
+                    }
+                }
+            }
+        }
+    },
+
+    oncollapse: function()
+    {
+        if (this._revertElement.parentElement)
+            this._revertElement.parentElement.removeChild(this._revertElement);
+    },
+
+    /**
+     * @param {?number} baseLineNumber
+     * @param {?number} newLineNumber
+     * @param {string} lineContent
+     * @param {string} changeType
+     */
+    _createLine: function(baseLineNumber, newLineNumber, lineContent, changeType)
+    {
+        var child = new TreeElement("", null, false);
+        child.selectable = false;
+        this.appendChild(child);
+        var lineElement = document.createElement("span");
+
+        function appendLineNumber(lineNumber)
+        {
+            var numberString = lineNumber !== null ? numberToStringWithSpacesPadding(lineNumber + 1, 4) : "    ";
+            var lineNumberSpan = document.createElement("span");
+            lineNumberSpan.addStyleClass("webkit-line-number");
+            lineNumberSpan.textContent = numberString;
+            child.listItemElement.appendChild(lineNumberSpan);
+        }
+
+        appendLineNumber(baseLineNumber);
+        appendLineNumber(newLineNumber);
+
+        var contentSpan = document.createElement("span");
+        contentSpan.textContent = lineContent;
+        child.listItemElement.appendChild(contentSpan);
+        child.listItemElement.addStyleClass("revision-history-line");
+        child.listItemElement.addStyleClass("revision-history-line-" + changeType);
+    },
+
+    allowRevert: function()
+    {
+        this._revertElement.removeStyleClass("hidden");
+    },
+
+    __proto__: TreeElement.prototype
+}
diff --git a/Source/devtools/front_end/RuntimeModel.js b/Source/devtools/front_end/RuntimeModel.js
new file mode 100644
index 0000000..e8b3e4f
--- /dev/null
+++ b/Source/devtools/front_end/RuntimeModel.js
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.ResourceTreeModel} resourceTreeModel
+ */
+WebInspector.RuntimeModel = function(resourceTreeModel)
+{
+    resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, this._frameAdded, this);
+    resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, this._frameNavigated, this);
+    resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this._frameDetached, this);
+    resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded, this._didLoadCachedResources, this);
+    this._frameIdToContextList = {};
+}
+
+WebInspector.RuntimeModel.Events = {
+    FrameExecutionContextListAdded: "FrameExecutionContextListAdded",
+    FrameExecutionContextListRemoved: "FrameExecutionContextListRemoved",
+}
+
+WebInspector.RuntimeModel.prototype = {
+    /**
+     * @param {WebInspector.ExecutionContext} executionContext
+     */
+    setCurrentExecutionContext: function(executionContext)
+    {
+        this._currentExecutionContext = executionContext;
+    },
+
+    /**
+     * @return {WebInspector.ExecutionContext}
+     */
+    currentExecutionContext: function()
+    {
+        return this._currentExecutionContext;
+    },
+
+    /**
+     * @return {Array.<WebInspector.FrameExecutionContextList>}
+     */
+    contextLists: function()
+    {
+        return Object.values(this._frameIdToContextList);
+    },
+
+    /**
+     * @param {WebInspector.ResourceTreeFrame} frame
+     * @return {WebInspector.FrameExecutionContextList}
+     */
+    contextListByFrame: function(frame)
+    {
+        return this._frameIdToContextList[frame.id];
+    },
+
+    _frameAdded: function(event)
+    {
+        var frame = event.data;
+        var context = new WebInspector.FrameExecutionContextList(frame);
+        this._frameIdToContextList[frame.id] = context;
+        this.dispatchEventToListeners(WebInspector.RuntimeModel.Events.FrameExecutionContextListAdded, context);
+    },
+
+    _frameNavigated: function(event)
+    {
+        var frame = event.data;
+        var context = this._frameIdToContextList[frame.id];
+        if (context)
+            context._frameNavigated(frame);
+    },
+
+    _frameDetached: function(event)
+    {
+        var frame = event.data;
+        var context = this._frameIdToContextList[frame.id];
+        if (!context)
+            return;
+        this.dispatchEventToListeners(WebInspector.RuntimeModel.Events.FrameExecutionContextListRemoved, context);
+        delete this._frameIdToContextList[frame.id];
+    },
+
+    _didLoadCachedResources: function()
+    {
+        InspectorBackend.registerRuntimeDispatcher(new WebInspector.RuntimeDispatcher(this));
+        RuntimeAgent.enable();
+    },
+
+    _executionContextCreated: function(context)
+    {
+        var contextList = this._frameIdToContextList[context.frameId];
+        // FIXME(85708): this should never happen
+        if (!contextList)
+            return;
+        contextList._addExecutionContext(new WebInspector.ExecutionContext(context.id, context.name, context.isPageContext));
+    },
+
+    /**
+     * @param {string} expression
+     * @param {string} objectGroup
+     * @param {boolean} includeCommandLineAPI
+     * @param {boolean} doNotPauseOnExceptionsAndMuteConsole
+     * @param {boolean} returnByValue
+     * @param {boolean} generatePreview
+     * @param {function(?WebInspector.RemoteObject, boolean, RuntimeAgent.RemoteObject=)} callback
+     */
+    evaluate: function(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
+    {
+        if (WebInspector.debuggerModel.selectedCallFrame()) {
+            WebInspector.debuggerModel.evaluateOnSelectedCallFrame(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback);
+            return;
+        }
+
+        if (!expression) {
+            // There is no expression, so the completion should happen against global properties.
+            expression = "this";
+        }
+
+        /**
+         * @param {?Protocol.Error} error
+         * @param {RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function evalCallback(error, result, wasThrown)
+        {
+            if (error) {
+                console.error(error);
+                callback(null, false);
+                return;
+            }
+
+            if (returnByValue)
+                callback(null, !!wasThrown, wasThrown ? null : result);
+            else
+                callback(WebInspector.RemoteObject.fromPayload(result), !!wasThrown);
+        }
+        RuntimeAgent.evaluate(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, this._currentExecutionContext ? this._currentExecutionContext.id : undefined, returnByValue, generatePreview, evalCallback);
+    },
+
+    /**
+     * @param {Element} proxyElement
+     * @param {Range} wordRange
+     * @param {boolean} force
+     * @param {function(!Array.<string>, number=)} completionsReadyCallback
+     */
+    completionsForTextPrompt: function(proxyElement, wordRange, force, completionsReadyCallback)
+    {
+        // Pass less stop characters to rangeOfWord so the range will be a more complete expression.
+        var expressionRange = wordRange.startContainer.rangeOfWord(wordRange.startOffset, " =:[({;,!+-*/&|^<>", proxyElement, "backward");
+        var expressionString = expressionRange.toString();
+        var prefix = wordRange.toString();
+        this._completionsForExpression(expressionString, prefix, force, completionsReadyCallback);
+    },
+
+    /**
+     * @param {string} expressionString
+     * @param {string} prefix
+     * @param {boolean} force
+     * @param {function(!Array.<string>, number=)} completionsReadyCallback
+     */
+    _completionsForExpression: function(expressionString, prefix, force, completionsReadyCallback)
+    {
+        var lastIndex = expressionString.length - 1;
+
+        var dotNotation = (expressionString[lastIndex] === ".");
+        var bracketNotation = (expressionString[lastIndex] === "[");
+
+        if (dotNotation || bracketNotation)
+            expressionString = expressionString.substr(0, lastIndex);
+
+        if (expressionString && parseInt(expressionString, 10) == expressionString) {
+            // User is entering float value, do not suggest anything.
+            completionsReadyCallback([]);
+            return;
+        }
+
+        if (!prefix && !expressionString && !force) {
+            completionsReadyCallback([]);
+            return;
+        }
+
+        if (!expressionString && WebInspector.debuggerModel.selectedCallFrame())
+            WebInspector.debuggerModel.getSelectedCallFrameVariables(receivedPropertyNames.bind(this));
+        else
+            this.evaluate(expressionString, "completion", true, true, false, false, evaluated.bind(this));
+
+        function evaluated(result, wasThrown)
+        {
+            if (!result || wasThrown) {
+                completionsReadyCallback([]);
+                return;
+            }
+
+            function getCompletions(primitiveType)
+            {
+                var object;
+                if (primitiveType === "string")
+                    object = new String("");
+                else if (primitiveType === "number")
+                    object = new Number(0);
+                else if (primitiveType === "boolean")
+                    object = new Boolean(false);
+                else
+                    object = this;
+
+                var resultSet = {};
+                for (var o = object; o; o = o.__proto__) {
+                    try {
+                        var names = Object.getOwnPropertyNames(o);
+                        for (var i = 0; i < names.length; ++i)
+                            resultSet[names[i]] = true;
+                    } catch (e) {
+                    }
+                }
+                return resultSet;
+            }
+
+            if (result.type === "object" || result.type === "function")
+                result.callFunctionJSON(getCompletions, undefined, receivedPropertyNames.bind(this));
+            else if (result.type === "string" || result.type === "number" || result.type === "boolean")
+                this.evaluate("(" + getCompletions + ")(\"" + result.type + "\")", "completion", false, true, true, false, receivedPropertyNamesFromEval.bind(this));
+        }
+
+        function receivedPropertyNamesFromEval(notRelevant, wasThrown, result)
+        {
+            if (result && !wasThrown)
+                receivedPropertyNames.call(this, result.value);
+            else
+                completionsReadyCallback([]);
+        }
+
+        function receivedPropertyNames(propertyNames)
+        {
+            RuntimeAgent.releaseObjectGroup("completion");
+            if (!propertyNames) {
+                completionsReadyCallback([]);
+                return;
+            }
+            var includeCommandLineAPI = (!dotNotation && !bracketNotation);
+            if (includeCommandLineAPI) {
+                const commandLineAPI = ["dir", "dirxml", "keys", "values", "profile", "profileEnd", "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear",
+                    "getEventListeners", "table", "$", "$$", "$x"];
+                for (var i = 0; i < commandLineAPI.length; ++i)
+                    propertyNames[commandLineAPI[i]] = true;
+            }
+            this._reportCompletions(completionsReadyCallback, dotNotation, bracketNotation, expressionString, prefix, Object.keys(propertyNames));
+        }
+    },
+
+    /**
+     * @param {function(!Array.<string>, number=)} completionsReadyCallback
+     * @param {boolean} dotNotation
+     * @param {boolean} bracketNotation
+     * @param {string} expressionString
+     * @param {string} prefix
+     * @param {Array.<string>} properties
+     */
+    _reportCompletions: function(completionsReadyCallback, dotNotation, bracketNotation, expressionString, prefix, properties) {
+        if (bracketNotation) {
+            if (prefix.length && prefix[0] === "'")
+                var quoteUsed = "'";
+            else
+                var quoteUsed = "\"";
+        }
+
+        var results = [];
+
+        if (!expressionString) {
+            const keywords = ["break", "case", "catch", "continue", "default", "delete", "do", "else", "finally", "for", "function", "if", "in",
+                              "instanceof", "new", "return", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with"];
+            properties = properties.concat(keywords);
+        }
+
+        properties.sort();
+
+        for (var i = 0; i < properties.length; ++i) {
+            var property = properties[i];
+
+            if (dotNotation && !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(property))
+                continue;
+
+            if (bracketNotation) {
+                if (!/^[0-9]+$/.test(property))
+                    property = quoteUsed + property.escapeCharacters(quoteUsed + "\\") + quoteUsed;
+                property += "]";
+            }
+
+            if (property.length < prefix.length)
+                continue;
+            if (prefix.length && !property.startsWith(prefix))
+                continue;
+
+            results.push(property);
+        }
+        completionsReadyCallback(results);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @type {WebInspector.RuntimeModel}
+ */
+WebInspector.runtimeModel = null;
+
+/**
+ * @constructor
+ * @implements {RuntimeAgent.Dispatcher}
+ * @param {WebInspector.RuntimeModel} runtimeModel
+ */
+WebInspector.RuntimeDispatcher = function(runtimeModel)
+{
+    this._runtimeModel = runtimeModel;
+}
+
+WebInspector.RuntimeDispatcher.prototype = {
+    executionContextCreated: function(context)
+    {
+        this._runtimeModel._executionContextCreated(context);
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.ExecutionContext = function(id, name, isPageContext)
+{
+    this.id = id;
+    this.name = (isPageContext && !name) ? "<page context>" : name;
+    this.isMainWorldContext = isPageContext;
+}
+
+/**
+ * @param {*} a
+ * @param {*} b
+ * @return {number}
+ */
+WebInspector.ExecutionContext.comparator = function(a, b)
+{
+    // Main world context should always go first.
+    if (a.isMainWorldContext)
+        return -1;
+    if (b.isMainWorldContext)
+        return +1;
+    return a.name.localeCompare(b.name);
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.FrameExecutionContextList = function(frame)
+{
+    this._frame = frame;
+    this._executionContexts = [];
+}
+
+WebInspector.FrameExecutionContextList.EventTypes = {
+    ContextsUpdated: "ContextsUpdated",
+    ContextAdded: "ContextAdded"
+}
+
+WebInspector.FrameExecutionContextList.prototype =
+{
+    _frameNavigated: function(frame)
+    {
+        this._frame = frame;
+        this._executionContexts = [];
+        this.dispatchEventToListeners(WebInspector.FrameExecutionContextList.EventTypes.ContextsUpdated, this);
+    },
+
+    /**
+     * @param {WebInspector.ExecutionContext} context
+     */
+    _addExecutionContext: function(context)
+    {
+        var insertAt = insertionIndexForObjectInListSortedByFunction(context, this._executionContexts, WebInspector.ExecutionContext.comparator);
+        this._executionContexts.splice(insertAt, 0, context);
+        this.dispatchEventToListeners(WebInspector.FrameExecutionContextList.EventTypes.ContextAdded, this);
+    },
+
+    executionContexts: function()
+    {
+        return this._executionContexts;
+    },
+
+    mainWorldContext: function() 
+    {
+        return this._executionContexts[0];
+    },
+
+    /**
+     * @param {string} securityOrigin
+     */
+    contextBySecurityOrigin: function(securityOrigin)
+    {
+        for (var i = 0; i < this._executionContexts.length; ++i) {
+            var context = this._executionContexts[i];
+            if (!context.isMainWorldContext && context.name === securityOrigin)
+                return context; 
+        }
+    },
+
+    get frameId()
+    {
+        return this._frame.id;
+    },
+
+    get url()
+    {
+        return this._frame.url;
+    },
+
+    get displayName()
+    {
+        if (!this._frame.parentFrame)
+            return "<top frame>";
+        var name = this._frame.name || "";
+        var subtitle = new WebInspector.ParsedURL(this._frame.url).displayName;
+        if (subtitle) {
+            if (!name)
+                return subtitle;
+            return name + "( " + subtitle + " )";
+        }
+        return "<iframe>";
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
diff --git a/Source/devtools/front_end/SASSSourceMapping.js b/Source/devtools/front_end/SASSSourceMapping.js
new file mode 100644
index 0000000..9c37652
--- /dev/null
+++ b/Source/devtools/front_end/SASSSourceMapping.js
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.SourceMapping}
+ * @param {WebInspector.CSSStyleModel} cssModel
+ * @param {WebInspector.Workspace} workspace
+ * @param {WebInspector.SimpleWorkspaceProvider} networkWorkspaceProvider
+ */
+WebInspector.SASSSourceMapping = function(cssModel, workspace, networkWorkspaceProvider)
+{
+    this._cssModel = cssModel;
+    this._workspace = workspace;
+    this._networkWorkspaceProvider = networkWorkspaceProvider;
+    this._sourceMapByURL = {};
+    this._sourceMapByStyleSheetURL = {};
+    this._cssURLsForSASSURL = {};
+    this._timeoutForURL = {};
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this);
+    WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventTypes.SavedURL, this._fileSaveFinished, this);
+    this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetChanged, this);
+    this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._reset, this);
+}
+
+WebInspector.SASSSourceMapping.prototype = {
+    _populate: function()
+    {
+        function populateFrame(frame)
+        {
+            for (var i = 0; i < frame.childFrames.length; ++i)
+                populateFrame.call(this, frame.childFrames[i]);
+
+            var resources = frame.resources();
+            for (var i = 0; i < resources.length; ++i)
+                this._resourceAdded({data:resources[i]});
+        }
+
+        populateFrame.call(this, WebInspector.resourceTreeModel.mainFrame);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _styleSheetChanged: function(event)
+    {
+        var isAddingRevision = this._isAddingRevision;
+        delete this._isAddingRevision;
+
+        if (isAddingRevision)
+            return;
+        var url = this._cssModel.resourceBinding().resourceURLForStyleSheetId(event.data.styleSheetId);
+        if (!url)
+            return;
+        this._cssModel.setSourceMapping(url, null);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _fileSaveFinished: function(event)
+    {
+        // FIXME: add support for FileMapping.
+        var sassURL = /** @type {string} */ (event.data);
+        function callback()
+        {
+            delete this._timeoutForURL[sassURL];
+            var cssURLs = this._cssURLsForSASSURL[sassURL];
+            if (!cssURLs)
+                return;
+            for (var i = 0; i < cssURLs.length; ++i)
+                this._reloadCSS(cssURLs[i]);
+        }
+
+        var timer = this._timeoutForURL[sassURL];
+        if (timer) {
+            clearTimeout(timer);
+            delete this._timeoutForURL[sassURL];
+        }
+        if (!WebInspector.settings.cssReloadEnabled.get() || !this._cssURLsForSASSURL[sassURL])
+            return;
+        var timeout = WebInspector.settings.cssReloadTimeout.get();
+        if (timeout && isFinite(timeout))
+            this._timeoutForURL[sassURL] = setTimeout(callback.bind(this), Number(timeout));
+    },
+
+    _reloadCSS: function(url)
+    {
+        var uiSourceCode = this._workspace.uiSourceCodeForURL(url);
+        if (!uiSourceCode)
+            return;
+        var newContent = InspectorFrontendHost.loadResourceSynchronously(url);
+        this._isAddingRevision = true;
+        uiSourceCode.addRevision(newContent);
+        // this._isAddingRevision will be deleted in this._styleSheetChanged().
+        this._loadAndProcessSourceMap(newContent, url, true);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _resourceAdded: function(event)
+    {
+        var resource = /** @type {WebInspector.Resource} */ (event.data);
+        if (resource.type !== WebInspector.resourceTypes.Stylesheet)
+            return;
+
+        /**
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function didRequestContent(content, contentEncoded, mimeType)
+        {
+            this._loadAndProcessSourceMap(content, resource.url);
+        }
+        resource.requestContent(didRequestContent.bind(this));
+    },
+
+    /**
+     * @param {?string} content
+     * @param {string} cssURL
+     * @param {boolean=} forceRebind
+     */
+    _loadAndProcessSourceMap: function(content, cssURL, forceRebind)
+    {
+        if (!content)
+            return;
+        var lines = content.split(/\r?\n/);
+        if (!lines.length)
+            return;
+
+        const sourceMapRegex = /^\/\*@ sourceMappingURL=([^\s]+)\s*\*\/$/;
+        var lastLine = lines[lines.length - 1];
+        var match = lastLine.match(sourceMapRegex);
+        if (!match)
+            return;
+
+        if (!forceRebind && this._sourceMapByStyleSheetURL[cssURL])
+            return;
+        var sourceMap = this.loadSourceMapForStyleSheet(match[1], cssURL, forceRebind);
+
+        if (!sourceMap)
+            return;
+        this._sourceMapByStyleSheetURL[cssURL] = sourceMap;
+        this._bindUISourceCode(cssURL, sourceMap);
+    },
+
+    /**
+     * @param {string} cssURL
+     * @param {string} sassURL
+     */
+    _addCSSURLforSASSURL: function(cssURL, sassURL)
+    {
+        var cssURLs;
+        if (this._cssURLsForSASSURL.hasOwnProperty(sassURL))
+            cssURLs = this._cssURLsForSASSURL[sassURL];
+        else {
+            cssURLs = [];
+            this._cssURLsForSASSURL[sassURL] = cssURLs;
+        }
+        if (cssURLs.indexOf(cssURL) === -1)
+            cssURLs.push(cssURL);
+    },
+
+    /**
+     * @param {string} sourceMapURL
+     * @param {string} styleSheetURL
+     * @param {boolean=} forceReload
+     * @return {WebInspector.SourceMap}
+     */
+    loadSourceMapForStyleSheet: function(sourceMapURL, styleSheetURL, forceReload)
+    {
+        var completeStyleSheetURL = WebInspector.ParsedURL.completeURL(WebInspector.inspectedPageURL, styleSheetURL);
+        if (!completeStyleSheetURL)
+            return null;
+        var completeSourceMapURL = WebInspector.ParsedURL.completeURL(completeStyleSheetURL, sourceMapURL);
+        if (!completeSourceMapURL)
+            return null;
+        var sourceMap = this._sourceMapByURL[completeSourceMapURL];
+        if (sourceMap && !forceReload)
+            return sourceMap;
+        sourceMap = WebInspector.SourceMap.load(completeSourceMapURL, completeStyleSheetURL);
+        if (!sourceMap) {
+            delete this._sourceMapByURL[completeSourceMapURL];
+            return null;
+        }
+        this._sourceMapByURL[completeSourceMapURL] = sourceMap;
+        return sourceMap;
+    },
+
+    /**
+     * @param {string} rawURL
+     * @param {WebInspector.SourceMap} sourceMap
+     */
+    _bindUISourceCode: function(rawURL, sourceMap)
+    {
+        this._cssModel.setSourceMapping(rawURL, this);
+        var sources = sourceMap.sources();
+        for (var i = 0; i < sources.length; ++i) {
+            var url = sources[i];
+            if (!this._workspace.hasMappingForURL(url) && !this._workspace.uiSourceCodeForURL(url)) {
+                var content = InspectorFrontendHost.loadResourceSynchronously(url);
+                var contentProvider = new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Stylesheet, content, "text/x-scss");
+                var uiSourceCode = this._networkWorkspaceProvider.addFileForURL(url, contentProvider, true);
+                this._addCSSURLforSASSURL(rawURL, url);
+                uiSourceCode.setSourceMapping(this);
+            }
+        }
+    },
+
+    /**
+     * @param {WebInspector.RawLocation} rawLocation
+     * @return {WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(rawLocation)
+    {
+        var location = /** @type WebInspector.CSSLocation */ (rawLocation);
+        var entry;
+        var sourceMap = this._sourceMapByStyleSheetURL[location.url];
+        if (!sourceMap)
+            return null;
+        entry = sourceMap.findEntry(location.lineNumber, location.columnNumber);
+        if (!entry || entry.length === 2)
+            return null;
+        var uiSourceCode = this._workspace.uiSourceCodeForURL(entry[2]);
+        if (!uiSourceCode)
+            return null;
+        return new WebInspector.UILocation(uiSourceCode, entry[3], entry[4]);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.RawLocation}
+     */
+    uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
+    {
+        // FIXME: Implement this when ui -> raw mapping has clients.
+        return new WebInspector.CSSLocation(uiSourceCode.url || "", lineNumber, columnNumber);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isIdentity: function()
+    {
+        return false;
+    },
+
+    _reset: function()
+    {
+        this._sourceMapByURL = {};
+        this._sourceMapByStyleSheetURL = {};
+        this._populate();
+    }
+}
diff --git a/Source/devtools/front_end/ScopeChainSidebarPane.js b/Source/devtools/front_end/ScopeChainSidebarPane.js
new file mode 100644
index 0000000..5d4ef00
--- /dev/null
+++ b/Source/devtools/front_end/ScopeChainSidebarPane.js
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.ScopeChainSidebarPane = function()
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Scope Variables"));
+    this._sections = [];
+    this._expandedSections = {};
+    this._expandedProperties = [];
+}
+
+WebInspector.ScopeChainSidebarPane.prototype = {
+    update: function(callFrame)
+    {
+        this.bodyElement.removeChildren();
+
+        if (!callFrame) {
+            var infoElement = document.createElement("div");
+            infoElement.className = "info";
+            infoElement.textContent = WebInspector.UIString("Not Paused");
+            this.bodyElement.appendChild(infoElement);
+            return;
+        }
+
+        for (var i = 0; i < this._sections.length; ++i) {
+            var section = this._sections[i];
+            if (!section.title)
+                continue;
+            if (section.expanded)
+                this._expandedSections[section.title] = true;
+            else
+                delete this._expandedSections[section.title];
+        }
+
+        this._sections = [];
+
+        var foundLocalScope = false;
+        var scopeChain = callFrame.scopeChain;
+        for (var i = 0; i < scopeChain.length; ++i) {
+            var scope = scopeChain[i];
+            var title = null;
+            var subtitle = scope.object.description;
+            var emptyPlaceholder = null;
+            var extraProperties = null;
+            var declarativeScope;
+            
+            switch (scope.type) {
+                case "local":
+                    foundLocalScope = true;
+                    title = WebInspector.UIString("Local");
+                    emptyPlaceholder = WebInspector.UIString("No Variables");
+                    subtitle = null;
+                    if (callFrame.this)
+                        extraProperties = [ new WebInspector.RemoteObjectProperty("this", WebInspector.RemoteObject.fromPayload(callFrame.this)) ];
+                    if (i == 0) {
+                        var details = WebInspector.debuggerModel.debuggerPausedDetails();
+                        var exception = details.reason === WebInspector.DebuggerModel.BreakReason.Exception ? details.auxData : 0;
+                        if (exception) {
+                            extraProperties = extraProperties || [];
+                            var exceptionObject = /** @type {RuntimeAgent.RemoteObject} */ (exception);
+                            extraProperties.push(new WebInspector.RemoteObjectProperty("<exception>", WebInspector.RemoteObject.fromPayload(exceptionObject)));
+                        }
+                    }
+                    declarativeScope = true;
+                    break;
+                case "closure":
+                    title = WebInspector.UIString("Closure");
+                    emptyPlaceholder = WebInspector.UIString("No Variables");
+                    subtitle = null;
+                    declarativeScope = true;
+                    break;
+                case "catch":
+                    title = WebInspector.UIString("Catch");
+                    subtitle = null;
+                    declarativeScope = true;
+                    break;
+                case "with":
+                    title = WebInspector.UIString("With Block");
+                    declarativeScope = false;
+                    break;
+                case "global":
+                    title = WebInspector.UIString("Global");
+                    declarativeScope = false;
+                    break;
+            }
+
+            if (!title || title === subtitle)
+                subtitle = null;
+            
+            var scopeRef;
+            if (declarativeScope)
+                scopeRef = new WebInspector.ScopeRef(i, callFrame.id, undefined);
+            else
+                scopeRef = undefined;
+            
+
+            var section = new WebInspector.ObjectPropertiesSection(WebInspector.ScopeRemoteObject.fromPayload(scope.object, scopeRef), title, subtitle, emptyPlaceholder, true, extraProperties, WebInspector.ScopeVariableTreeElement);
+            section.editInSelectedCallFrameWhenPaused = true;
+            section.pane = this;
+
+            if (scope.type === "global")
+                section.expanded = false;
+            else if (!foundLocalScope || scope.type === "local" || title in this._expandedSections)
+                section.expanded = true;
+
+            this._sections.push(section);
+            this.bodyElement.appendChild(section.element);
+        }
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ObjectPropertyTreeElement}
+ * @param {WebInspector.RemoteObjectProperty} property
+ */
+WebInspector.ScopeVariableTreeElement = function(property)
+{
+    WebInspector.ObjectPropertyTreeElement.call(this, property);
+}
+
+WebInspector.ScopeVariableTreeElement.prototype = {
+    onattach: function()
+    {
+        WebInspector.ObjectPropertyTreeElement.prototype.onattach.call(this);
+        if (this.hasChildren && this.propertyIdentifier in this.treeOutline.section.pane._expandedProperties)
+            this.expand();
+    },
+
+    onexpand: function()
+    {
+        this.treeOutline.section.pane._expandedProperties[this.propertyIdentifier] = true;
+    },
+
+    oncollapse: function()
+    {
+        delete this.treeOutline.section.pane._expandedProperties[this.propertyIdentifier];
+    },
+
+    get propertyIdentifier()
+    {
+        if ("_propertyIdentifier" in this)
+            return this._propertyIdentifier;
+        var section = this.treeOutline.section;
+        this._propertyIdentifier = section.title + ":" + (section.subtitle ? section.subtitle + ":" : "") + this.propertyPath();
+        return this._propertyIdentifier;
+    },
+
+    __proto__: WebInspector.ObjectPropertyTreeElement.prototype
+}
diff --git a/Source/devtools/front_end/Script.js b/Source/devtools/front_end/Script.js
new file mode 100644
index 0000000..a183991
--- /dev/null
+++ b/Source/devtools/front_end/Script.js
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @implements {WebInspector.ContentProvider}
+ * @param {string} scriptId
+ * @param {string} sourceURL
+ * @param {number} startLine
+ * @param {number} startColumn
+ * @param {number} endLine
+ * @param {number} endColumn
+ * @param {boolean} isContentScript
+ * @param {string=} sourceMapURL
+ * @param {boolean=} hasSourceURL
+ */
+WebInspector.Script = function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL)
+{
+    this.scriptId = scriptId;
+    this.sourceURL = sourceURL;
+    this.lineOffset = startLine;
+    this.columnOffset = startColumn;
+    this.endLine = endLine;
+    this.endColumn = endColumn;
+    this.isContentScript = isContentScript;
+    this.sourceMapURL = sourceMapURL;
+    this.hasSourceURL = hasSourceURL;
+    this._locations = new Set();
+    this._sourceMappings = [];
+}
+
+WebInspector.Script.Events = {
+    ScriptEdited: "ScriptEdited",
+}
+
+WebInspector.Script.snippetSourceURLPrefix = "snippets:///";
+
+WebInspector.Script.prototype = {
+    /**
+     * @return {string}
+     */
+    contentURL: function()
+    {
+        return this.sourceURL;
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function()
+    {
+        return WebInspector.resourceTypes.Script;
+    },
+
+    /**
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestContent: function(callback)
+    {
+        if (this._source) {
+            callback(this._source, false, "text/javascript");
+            return;
+        }
+
+        /**
+         * @this {WebInspector.Script}
+         * @param {?Protocol.Error} error
+         * @param {string} source
+         */
+        function didGetScriptSource(error, source)
+        {
+            this._source = error ? "" : source;
+            callback(this._source, false, "text/javascript");
+        }
+        if (this.scriptId) {
+            // Script failed to parse.
+            DebuggerAgent.getScriptSource(this.scriptId, didGetScriptSource.bind(this));
+        } else
+            callback("", false, "text/javascript");
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<PageAgent.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback)
+    {
+        /**
+         * @this {WebInspector.Script}
+         * @param {?Protocol.Error} error
+         * @param {Array.<PageAgent.SearchMatch>} searchMatches
+         */
+        function innerCallback(error, searchMatches)
+        {
+            if (error)
+                console.error(error);
+            var result = [];
+            for (var i = 0; i < searchMatches.length; ++i) {
+                var searchMatch = new WebInspector.ContentProvider.SearchMatch(searchMatches[i].lineNumber, searchMatches[i].lineContent);
+                result.push(searchMatch);
+            }
+            callback(result || []);
+        }
+
+        if (this.scriptId) {
+            // Script failed to parse.
+            DebuggerAgent.searchInContent(this.scriptId, query, caseSensitive, isRegex, innerCallback.bind(this));
+        } else
+            callback([]);
+    },
+
+    /**
+     * @param {string} newSource
+     * @param {function(?Protocol.Error, Array.<DebuggerAgent.CallFrame>=)} callback
+     */
+    editSource: function(newSource, callback)
+    {
+        /**
+         * @this {WebInspector.Script}
+         * @param {?Protocol.Error} error
+         * @param {Array.<DebuggerAgent.CallFrame>=} callFrames
+         * @param {Object=} debugData
+         */
+        function didEditScriptSource(error, callFrames, debugData)
+        {
+            // FIXME: support debugData.stack_update_needs_step_in flag by calling WebInspector.debugger_model.callStackModified
+            if (!error)
+                this._source = newSource;
+            callback(error, callFrames);
+            if (!error)
+                this.dispatchEventToListeners(WebInspector.Script.Events.ScriptEdited, newSource);
+        }
+        if (this.scriptId) {
+            // Script failed to parse.
+            DebuggerAgent.setScriptSource(this.scriptId, newSource, undefined, didEditScriptSource.bind(this));
+        } else
+            callback("Script failed to parse");
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isInlineScript: function()
+    {
+        var startsAtZero = !this.lineOffset && !this.columnOffset;
+        return !!this.sourceURL && !startsAtZero;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isAnonymousScript: function()
+    {
+        return !this.sourceURL;
+    },
+
+    /**
+     * @param {boolean} isDynamicScript
+     */
+    setIsDynamicScript: function(isDynamicScript)
+    {
+        this._isDynamicScript = isDynamicScript;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isDynamicScript: function()
+    {
+        return !!this._isDynamicScript;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isSnippet: function()
+    {
+        return this.sourceURL && this.sourceURL.startsWith(WebInspector.Script.snippetSourceURLPrefix);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     * @return {WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(lineNumber, columnNumber)
+    {
+        var uiLocation;
+        var rawLocation = new WebInspector.DebuggerModel.Location(this.scriptId, lineNumber, columnNumber || 0);
+        for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i)
+            uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation);
+        console.assert(uiLocation, "Script raw location can not be mapped to any ui location.");
+        return uiLocation.uiSourceCode.overrideLocation(uiLocation);
+    },
+
+    /**
+     * @param {WebInspector.SourceMapping} sourceMapping
+     */
+    pushSourceMapping: function(sourceMapping)
+    {
+        this._sourceMappings.push(sourceMapping);
+        this.updateLocations();
+    },
+
+    popSourceMapping: function()
+    {
+        this._sourceMappings.pop();
+        this.updateLocations();
+    },
+
+    updateLocations: function()
+    {
+        var items = this._locations.items();
+        for (var i = 0; i < items.length; ++i)
+            items[i].update();
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} rawLocation
+     * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
+     * @return {WebInspector.Script.Location}
+     */
+    createLiveLocation: function(rawLocation, updateDelegate)
+    {
+        console.assert(rawLocation.scriptId === this.scriptId);
+        var location = new WebInspector.Script.Location(this, rawLocation, updateDelegate);
+        this._locations.add(location);
+        location.update();
+        return location;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.LiveLocation}
+ * @param {WebInspector.Script} script
+ * @param {WebInspector.DebuggerModel.Location} rawLocation
+ * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
+ */
+WebInspector.Script.Location = function(script, rawLocation, updateDelegate)
+{
+    WebInspector.LiveLocation.call(this, rawLocation, updateDelegate);
+    this._script = script;
+}
+
+WebInspector.Script.Location.prototype = {
+    /**
+     * @return {WebInspector.UILocation}
+     */
+    uiLocation: function()
+    {
+        var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this.rawLocation());
+        return this._script.rawLocationToUILocation(debuggerModelLocation.lineNumber, debuggerModelLocation.columnNumber);
+    },
+
+    dispose: function()
+    {
+        WebInspector.LiveLocation.prototype.dispose.call(this);
+        this._script._locations.remove(this);
+    },
+
+    __proto__: WebInspector.LiveLocation.prototype
+}
diff --git a/Source/devtools/front_end/ScriptFormatter.js b/Source/devtools/front_end/ScriptFormatter.js
new file mode 100644
index 0000000..3eedeba
--- /dev/null
+++ b/Source/devtools/front_end/ScriptFormatter.js
@@ -0,0 +1,270 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.Formatter = function()
+{
+}
+
+/**
+ * @param {WebInspector.ResourceType} contentType
+ * @return {?WebInspector.Formatter}
+ */
+WebInspector.Formatter.createFormatter = function(contentType)
+{
+    if (contentType === WebInspector.resourceTypes.Script || contentType === WebInspector.resourceTypes.Document)
+        return new WebInspector.ScriptFormatter();
+    return new WebInspector.IdentityFormatter();
+}
+
+/**
+ * @param {Array.<number>} lineEndings
+ * @param {number} lineNumber
+ * @param {number} columnNumber
+ * @return {number}
+ */
+WebInspector.Formatter.locationToPosition = function(lineEndings, lineNumber, columnNumber)
+{
+    var position = lineNumber ? lineEndings[lineNumber - 1] + 1 : 0;
+    return position + columnNumber;
+}
+
+/**
+ * @param {Array.<number>} lineEndings
+ * @param {number} position
+ * @return {Array.<number>}
+ */
+WebInspector.Formatter.positionToLocation = function(lineEndings, position)
+{
+    var lineNumber = lineEndings.upperBound(position - 1);
+    if (!lineNumber)
+        var columnNumber = position;
+    else
+        var columnNumber = position - lineEndings[lineNumber - 1] - 1;
+    return [lineNumber, columnNumber];
+}
+
+WebInspector.Formatter.prototype = {
+    /**
+     * @param {string} mimeType
+     * @param {string} content
+     * @param {function(string, WebInspector.FormatterSourceMapping)} callback
+     */
+    formatContent: function(mimeType, content, callback)
+    {
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.Formatter}
+ */
+WebInspector.ScriptFormatter = function()
+{
+    this._tasks = [];
+}
+
+WebInspector.ScriptFormatter.prototype = {
+    /**
+     * @param {string} mimeType
+     * @param {string} content
+     * @param {function(string, WebInspector.FormatterSourceMapping)} callback
+     */
+    formatContent: function(mimeType, content, callback)
+    {
+        content = content.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '');
+        const method = "format";
+        var parameters = { mimeType: mimeType, content: content, indentString: WebInspector.settings.textEditorIndent.get() };
+        this._tasks.push({ data: parameters, callback: callback });
+        this._worker.postMessage({ method: method, params: parameters });
+    },
+
+    _didFormatContent: function(event)
+    {
+        var task = this._tasks.shift();
+        var originalContent = task.data.content;
+        var formattedContent = event.data.content;
+        var mapping = event.data["mapping"];
+        var sourceMapping = new WebInspector.FormatterSourceMappingImpl(originalContent.lineEndings(), formattedContent.lineEndings(), mapping);
+        task.callback(formattedContent, sourceMapping);
+    },
+
+    /**
+     * @return {Worker}
+     */
+    get _worker()
+    {
+        if (!this._cachedWorker) {
+            this._cachedWorker = new Worker("ScriptFormatterWorker.js");
+            this._cachedWorker.onmessage = /** @type {function(this:Worker)} */ (this._didFormatContent.bind(this));
+        }
+        return this._cachedWorker;
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.Formatter}
+ */
+WebInspector.IdentityFormatter = function()
+{
+    this._tasks = [];
+}
+
+WebInspector.IdentityFormatter.prototype = {
+    /**
+     * @param {string} mimeType
+     * @param {string} content
+     * @param {function(string, WebInspector.FormatterSourceMapping)} callback
+     */
+    formatContent: function(mimeType, content, callback)
+    {
+        callback(content, new WebInspector.IdentityFormatterSourceMapping());
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.FormatterMappingPayload = function()
+{
+    this.original = [];
+    this.formatted = [];
+}
+
+/**
+ * @interface
+ */
+WebInspector.FormatterSourceMapping = function()
+{
+}
+
+WebInspector.FormatterSourceMapping.prototype = {
+    /**
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     * @return {Array.<number>}
+     */
+    originalToFormatted: function(lineNumber, columnNumber) { },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     * @return {Array.<number>}
+     */
+    formattedToOriginal: function(lineNumber, columnNumber) { }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.FormatterSourceMapping}
+ */
+WebInspector.IdentityFormatterSourceMapping = function()
+{
+}
+
+WebInspector.IdentityFormatterSourceMapping.prototype = {
+    /**
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     * @return {Array.<number>}
+     */
+    originalToFormatted: function(lineNumber, columnNumber)
+    {
+        return [lineNumber, columnNumber || 0]; 
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     * @return {Array.<number>}
+     */
+    formattedToOriginal: function(lineNumber, columnNumber)
+    {
+        return [lineNumber, columnNumber || 0];
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.FormatterSourceMapping}
+ * @param {Array.<number>} originalLineEndings
+ * @param {Array.<number>} formattedLineEndings
+ * @param {WebInspector.FormatterMappingPayload} mapping
+ */
+WebInspector.FormatterSourceMappingImpl = function(originalLineEndings, formattedLineEndings, mapping)
+{
+    this._originalLineEndings = originalLineEndings;
+    this._formattedLineEndings = formattedLineEndings;
+    this._mapping = mapping;
+}
+
+WebInspector.FormatterSourceMappingImpl.prototype = {
+    /**
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     * @return {Array.<number>}
+     */
+    originalToFormatted: function(lineNumber, columnNumber)
+    {
+        var originalPosition = WebInspector.Formatter.locationToPosition(this._originalLineEndings, lineNumber, columnNumber || 0);
+        var formattedPosition = this._convertPosition(this._mapping.original, this._mapping.formatted, originalPosition || 0);
+        return WebInspector.Formatter.positionToLocation(this._formattedLineEndings, formattedPosition);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     * @return {Array.<number>}
+     */
+    formattedToOriginal: function(lineNumber, columnNumber)
+    {
+        var formattedPosition = WebInspector.Formatter.locationToPosition(this._formattedLineEndings, lineNumber, columnNumber || 0);
+        var originalPosition = this._convertPosition(this._mapping.formatted, this._mapping.original, formattedPosition);
+        return WebInspector.Formatter.positionToLocation(this._originalLineEndings, originalPosition || 0);
+    },
+
+    /**
+     * @param {Array.<number>} positions1
+     * @param {Array.<number>} positions2
+     * @param {number} position
+     * @return {number}
+     */
+    _convertPosition: function(positions1, positions2, position)
+    {
+        var index = positions1.upperBound(position) - 1;
+        var convertedPosition = positions2[index] + position - positions1[index];
+        if (index < positions2.length - 1 && convertedPosition > positions2[index + 1])
+            convertedPosition = positions2[index + 1];
+        return convertedPosition;
+    }
+}
diff --git a/Source/devtools/front_end/ScriptFormatterWorker.js b/Source/devtools/front_end/ScriptFormatterWorker.js
new file mode 100644
index 0000000..d394a2a
--- /dev/null
+++ b/Source/devtools/front_end/ScriptFormatterWorker.js
@@ -0,0 +1,235 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+onmessage = function(event) {
+    if (!event.data.method)
+        return;
+
+    self[event.data.method](event.data.params);
+};
+
+function format(params)
+{
+    // Default to a 4-space indent.
+    var indentString = params.indentString || "    ";
+    var result = {};
+
+    if (params.mimeType === "text/html") {
+        var formatter = new HTMLScriptFormatter(indentString);
+        result = formatter.format(params.content);
+    } else {
+        result.mapping = { original: [0], formatted: [0] };
+        result.content = formatScript(params.content, result.mapping, 0, 0, indentString);
+    }
+    postMessage(result);
+}
+
+function getChunkCount(totalLength, chunkSize)
+{
+    if (totalLength <= chunkSize)
+        return 1;
+
+    var remainder = totalLength % chunkSize;
+    var partialLength = totalLength - remainder;
+    return (partialLength / chunkSize) + (remainder ? 1 : 0);
+}
+
+function outline(params)
+{
+    const chunkSize = 100000; // characters per data chunk
+    const totalLength = params.content.length;
+    const lines = params.content.split("\n");
+    const chunkCount = getChunkCount(totalLength, chunkSize);
+    var outlineChunk = [];
+    var previousIdentifier = null;
+    var previousToken = null;
+    var previousTokenType = null;
+    var currentChunk = 1;
+    var processedChunkCharacters = 0;
+    var addedFunction = false;
+    var isReadingArguments = false;
+    var argumentsText = "";
+    var currentFunction = null;
+    var scriptTokenizer = new WebInspector.SourceJavaScriptTokenizer();
+    scriptTokenizer.condition = scriptTokenizer.createInitialCondition();
+
+    for (var i = 0; i < lines.length; ++i) {
+        var line = lines[i];
+        var column = 0;
+        scriptTokenizer.line = line;
+        do {
+            var newColumn = scriptTokenizer.nextToken(column);
+            var tokenType = scriptTokenizer.tokenType;
+            var tokenValue = line.substring(column, newColumn);
+            if (tokenType === "javascript-ident") {
+                previousIdentifier = tokenValue;
+                if (tokenValue && previousToken === "function") {
+                    // A named function: "function f...".
+                    currentFunction = { line: i, name: tokenValue };
+                    addedFunction = true;
+                    previousIdentifier = null;
+                }
+            } else if (tokenType === "javascript-keyword") {
+                if (tokenValue === "function") {
+                    if (previousIdentifier && (previousToken === "=" || previousToken === ":")) {
+                        // Anonymous function assigned to an identifier: "...f = function..."
+                        // or "funcName: function...".
+                        currentFunction = { line: i, name: previousIdentifier };
+                        addedFunction = true;
+                        previousIdentifier = null;
+                    }
+                }
+            } else if (tokenValue === "." && previousTokenType === "javascript-ident")
+                previousIdentifier += ".";
+            else if (tokenValue === "(" && addedFunction)
+                isReadingArguments = true;
+            if (isReadingArguments && tokenValue)
+                argumentsText += tokenValue;
+
+            if (tokenValue === ")" && isReadingArguments) {
+                addedFunction = false;
+                isReadingArguments = false;
+                currentFunction.arguments = argumentsText.replace(/,[\r\n\s]*/g, ", ").replace(/([^,])[\r\n\s]+/g, "$1");
+                argumentsText = "";
+                outlineChunk.push(currentFunction);
+            }
+
+            if (tokenValue.trim().length) {
+                // Skip whitespace tokens.
+                previousToken = tokenValue;
+                previousTokenType = tokenType;
+            }
+            processedChunkCharacters += newColumn - column;
+            column = newColumn;
+
+            if (processedChunkCharacters >= chunkSize) {
+                postMessage({ chunk: outlineChunk, total: chunkCount, index: currentChunk++ });
+                outlineChunk = [];
+                processedChunkCharacters = 0;
+            }
+        } while (column < line.length);
+    }
+    postMessage({ chunk: outlineChunk, total: chunkCount, index: chunkCount });
+}
+
+function formatScript(content, mapping, offset, formattedOffset, indentString)
+{
+    var formattedContent;
+    try {
+        var tokenizer = new Tokenizer(content);
+        var builder = new FormattedContentBuilder(tokenizer.content(), mapping, offset, formattedOffset, indentString);
+        var formatter = new JavaScriptFormatter(tokenizer, builder);
+        formatter.format();
+        formattedContent = builder.content();
+    } catch (e) {
+        formattedContent = content;
+    }
+    return formattedContent;
+}
+
+WebInspector = {};
+
+Array.prototype.keySet = function()
+{
+    var keys = {};
+    for (var i = 0; i < this.length; ++i)
+        keys[this[i]] = true;
+    return keys;
+};
+
+importScripts("SourceTokenizer.js");
+importScripts("SourceHTMLTokenizer.js");
+importScripts("SourceJavaScriptTokenizer.js");
+
+HTMLScriptFormatter = function(indentString)
+{
+    WebInspector.SourceHTMLTokenizer.call(this);
+    this._indentString = indentString;
+}
+
+HTMLScriptFormatter.prototype = {
+    format: function(content)
+    {
+        this.line = content;
+        this._content = content;
+        this._formattedContent = "";
+        this._mapping = { original: [0], formatted: [0] };
+        this._position = 0;
+
+        var cursor = 0;
+        while (cursor < this._content.length)
+            cursor = this.nextToken(cursor);
+
+        this._formattedContent += this._content.substring(this._position);
+        return { content: this._formattedContent, mapping: this._mapping };
+    },
+
+    scriptStarted: function(cursor)
+    {
+        this._formattedContent += this._content.substring(this._position, cursor);
+        this._formattedContent += "\n";
+        this._position = cursor;
+    },
+
+    scriptEnded: function(cursor)
+    {
+        if (cursor === this._position)
+            return;
+
+        var scriptContent = this._content.substring(this._position, cursor);
+        this._mapping.original.push(this._position);
+        this._mapping.formatted.push(this._formattedContent.length);
+        var formattedScriptContent = formatScript(scriptContent, this._mapping, this._position, this._formattedContent.length, this._indentString);
+
+        this._formattedContent += formattedScriptContent;
+        this._position = cursor;
+    },
+
+    styleSheetStarted: function(cursor)
+    {
+    },
+
+    styleSheetEnded: function(cursor)
+    {
+    },
+
+    __proto__: WebInspector.SourceHTMLTokenizer.prototype
+}
+
+function require()
+{
+    return parse;
+}
+
+var exports = {};
+importScripts("UglifyJS/parse-js.js");
+var parse = exports;
+
+importScripts("JavaScriptFormatter.js");
diff --git a/Source/devtools/front_end/ScriptSnippetModel.js b/Source/devtools/front_end/ScriptSnippetModel.js
new file mode 100644
index 0000000..8ef398b
--- /dev/null
+++ b/Source/devtools/front_end/ScriptSnippetModel.js
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.ScriptSnippetModel = function(workspace)
+{
+    this._workspace = workspace;
+    /** {Object.<string, WebInspector.UISourceCode>} */
+    this._uiSourceCodeForScriptId = {};
+    this._scriptForUISourceCode = new Map();
+    /** {Object.<string, WebInspector.UISourceCode>} */
+    this._uiSourceCodeForSnippetId = {};
+    this._snippetIdForUISourceCode = new Map();
+    
+    this._snippetStorage = new WebInspector.SnippetStorage("script", "Script snippet #");
+    this._lastSnippetEvaluationIndexSetting = WebInspector.settings.createSetting("lastSnippetEvaluationIndex", 0);
+    this._snippetScriptMapping = new WebInspector.SnippetScriptMapping(this);
+    this._workspaceProvider = new WebInspector.SimpleWorkspaceProvider(this._workspace, WebInspector.projectTypes.Snippets);
+    this.reset();
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
+}
+
+WebInspector.ScriptSnippetModel.prototype = {
+    /**
+     * @return {WebInspector.SnippetScriptMapping}
+     */
+    get scriptMapping()
+    {
+        return this._snippetScriptMapping;
+    },
+
+    _loadSnippets: function()
+    {
+        var snippets = this._snippetStorage.snippets();
+        for (var i = 0; i < snippets.length; ++i)
+            this._addScriptSnippet(snippets[i]);
+    },
+
+    /**
+     * @return {WebInspector.UISourceCode}
+     */
+    createScriptSnippet: function()
+    {
+        var snippet = this._snippetStorage.createSnippet();
+        return this._addScriptSnippet(snippet);
+    },
+
+    /**
+     * @param {WebInspector.Snippet} snippet
+     * @return {WebInspector.UISourceCode}
+     */
+    _addScriptSnippet: function(snippet)
+    {
+        var uiSourceCode = this._workspaceProvider.addFileByName("", snippet.name, new WebInspector.SnippetContentProvider(snippet), true);
+        var scriptFile = new WebInspector.SnippetScriptFile(this, uiSourceCode);
+        uiSourceCode.setScriptFile(scriptFile);
+        this._snippetIdForUISourceCode.put(uiSourceCode, snippet.id);
+        uiSourceCode.setSourceMapping(this._snippetScriptMapping);
+        this._uiSourceCodeForSnippetId[snippet.id] = uiSourceCode;
+        return uiSourceCode;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    deleteScriptSnippet: function(uiSourceCode)
+    {
+        var snippetId = this._snippetIdForUISourceCode.get(uiSourceCode);
+        var snippet = this._snippetStorage.snippetForId(snippetId);
+        this._snippetStorage.deleteSnippet(snippet);
+        this._removeBreakpoints(uiSourceCode);
+        this._releaseSnippetScript(uiSourceCode);
+        delete this._uiSourceCodeForSnippetId[snippet.id];
+        this._snippetIdForUISourceCode.remove(uiSourceCode);
+        this._workspaceProvider.removeFileByName("", snippet.name);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {string} newName
+     */
+    renameScriptSnippet: function(uiSourceCode, newName)
+    {
+        var breakpointLocations = this._removeBreakpoints(uiSourceCode);
+        var snippetId = this._snippetIdForUISourceCode.get(uiSourceCode);
+        var snippet = this._snippetStorage.snippetForId(snippetId);
+        if (!snippet || !newName || snippet.name === newName)
+            return;
+        snippet.name = newName;
+        this._restoreBreakpoints(uiSourceCode, breakpointLocations);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {string} newContent
+     */
+    _setScriptSnippetContent: function(uiSourceCode, newContent)
+    {
+        var snippetId = this._snippetIdForUISourceCode.get(uiSourceCode);
+        var snippet = this._snippetStorage.snippetForId(snippetId);
+        snippet.content = newContent;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _scriptSnippetEdited: function(uiSourceCode)
+    {
+        var script = this._scriptForUISourceCode.get(uiSourceCode);
+        if (!script)
+            return;
+
+        var breakpointLocations = this._removeBreakpoints(uiSourceCode);
+        var scriptUISourceCode = this._releaseSnippetScript(uiSourceCode);
+        this._restoreBreakpoints(uiSourceCode, breakpointLocations);
+        if (scriptUISourceCode)
+            this._restoreBreakpoints(scriptUISourceCode, breakpointLocations);
+    },
+
+    /**
+     * @param {string} snippetId
+     * @return {number}
+     */
+    _nextEvaluationIndex: function(snippetId)
+    {
+        var evaluationIndex = this._lastSnippetEvaluationIndexSetting.get() + 1;
+        this._lastSnippetEvaluationIndexSetting.set(evaluationIndex);
+        return evaluationIndex;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    evaluateScriptSnippet: function(uiSourceCode)
+    {
+        var breakpointLocations = this._removeBreakpoints(uiSourceCode);
+        this._releaseSnippetScript(uiSourceCode);
+        this._restoreBreakpoints(uiSourceCode, breakpointLocations);
+        var snippetId = this._snippetIdForUISourceCode.get(uiSourceCode);
+        var evaluationIndex = this._nextEvaluationIndex(snippetId);
+        uiSourceCode._evaluationIndex = evaluationIndex;
+        var evaluationUrl = this._evaluationSourceURL(uiSourceCode);
+
+        var expression = uiSourceCode.workingCopy();
+        
+        // In order to stop on the breakpoints during the snippet evaluation we need to compile and run it separately.
+        // If separate compilation and execution is not supported by the port we fall back to evaluation in console.
+        // In case we don't need that since debugger is already paused.
+        // We do the same when we are stopped on the call frame  since debugger is already paused and can not stop on breakpoint anymore.
+        if (WebInspector.debuggerModel.selectedCallFrame()) {
+            expression = uiSourceCode.workingCopy() + "\n//@ sourceURL=" + evaluationUrl + "\n";
+            WebInspector.evaluateInConsole(expression, true);
+            return;
+        }
+        
+        WebInspector.showConsole();
+        DebuggerAgent.compileScript(expression, evaluationUrl, compileCallback.bind(this));
+
+        /**
+         * @param {?string} error
+         * @param {string=} scriptId
+         * @param {string=} syntaxErrorMessage
+         */
+        function compileCallback(error, scriptId, syntaxErrorMessage)
+        {
+            if (!uiSourceCode || uiSourceCode._evaluationIndex !== evaluationIndex)
+                return;
+
+            if (error) {
+                console.error(error);
+                return;
+            }
+
+            if (!scriptId) {
+                var consoleMessage = WebInspector.ConsoleMessage.create(
+                        WebInspector.ConsoleMessage.MessageSource.JS,
+                        WebInspector.ConsoleMessage.MessageLevel.Error,
+                        syntaxErrorMessage || "");
+                WebInspector.console.addMessage(consoleMessage);
+                return;
+            }
+
+            var breakpointLocations = this._removeBreakpoints(uiSourceCode);
+            this._restoreBreakpoints(uiSourceCode, breakpointLocations);
+
+            this._runScript(scriptId);
+        }
+    },
+
+    /**
+     * @param {DebuggerAgent.ScriptId} scriptId
+     */
+    _runScript: function(scriptId)
+    {
+        var currentExecutionContext = WebInspector.runtimeModel.currentExecutionContext();
+        DebuggerAgent.runScript(scriptId, currentExecutionContext ? currentExecutionContext.id : undefined, "console", false, runCallback.bind(this));
+
+        /**
+         * @param {?string} error
+         * @param {?RuntimeAgent.RemoteObject} result
+         * @param {boolean=} wasThrown
+         */
+        function runCallback(error, result, wasThrown)
+        {
+            if (error) {
+                console.error(error);
+                return;
+            }
+
+            this._printRunScriptResult(result, wasThrown);
+        }
+    },
+
+    /**
+     * @param {?RuntimeAgent.RemoteObject} result
+     * @param {boolean=} wasThrown
+     */
+    _printRunScriptResult: function(result, wasThrown)
+    {
+        var level = (wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log);
+        var message = WebInspector.ConsoleMessage.create(WebInspector.ConsoleMessage.MessageSource.JS, level, "", undefined, undefined, undefined, undefined, [result]);
+        WebInspector.console.addMessage(message)
+    },
+
+    /**
+     * @param {WebInspector.DebuggerModel.Location} rawLocation
+     * @return {WebInspector.UILocation}
+     */
+    _rawLocationToUILocation: function(rawLocation)
+    {
+        var uiSourceCode = this._uiSourceCodeForScriptId[rawLocation.scriptId];
+        return new WebInspector.UILocation(uiSourceCode, rawLocation.lineNumber, rawLocation.columnNumber || 0);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.DebuggerModel.Location}
+     */
+    _uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
+    {
+        var script = this._scriptForUISourceCode.get(uiSourceCode);
+        if (!script)
+            return null;
+
+        return WebInspector.debuggerModel.createRawLocation(script, lineNumber, columnNumber);
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     */
+    _addScript: function(script)
+    {
+        var snippetId = this._snippetIdForSourceURL(script.sourceURL);
+        if (!snippetId)
+            return;
+        var uiSourceCode = this._uiSourceCodeForSnippetId[snippetId];
+
+        if (!uiSourceCode || this._evaluationSourceURL(uiSourceCode) !== script.sourceURL)
+            return;
+
+        console.assert(!this._scriptForUISourceCode.get(uiSourceCode));
+        this._uiSourceCodeForScriptId[script.scriptId] = uiSourceCode;
+        this._scriptForUISourceCode.put(uiSourceCode, script);
+        uiSourceCode.scriptFile().setHasDivergedFromVM(false);
+        script.pushSourceMapping(this._snippetScriptMapping);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {Array.<Object>}
+     */
+    _removeBreakpoints: function(uiSourceCode)
+    {
+        var breakpointLocations = WebInspector.breakpointManager.breakpointLocationsForUISourceCode(uiSourceCode);
+        for (var i = 0; i < breakpointLocations.length; ++i)
+            breakpointLocations[i].breakpoint.remove();
+        return breakpointLocations;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {Array.<Object>} breakpointLocations
+     */
+    _restoreBreakpoints: function(uiSourceCode, breakpointLocations)
+    {
+        for (var i = 0; i < breakpointLocations.length; ++i) {
+            var uiLocation = breakpointLocations[i].uiLocation;
+            var breakpoint = breakpointLocations[i].breakpoint;
+            WebInspector.breakpointManager.setBreakpoint(uiSourceCode, uiLocation.lineNumber, breakpoint.condition(), breakpoint.enabled());
+        }
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {WebInspector.UISourceCode}
+     */
+    _releaseSnippetScript: function(uiSourceCode)
+    {
+        var script = this._scriptForUISourceCode.get(uiSourceCode);
+        if (!script)
+            return null;
+
+        uiSourceCode.scriptFile().setIsDivergingFromVM(true);
+        uiSourceCode.scriptFile().setHasDivergedFromVM(true);
+        delete this._uiSourceCodeForScriptId[script.scriptId];
+        this._scriptForUISourceCode.remove(uiSourceCode);
+        delete uiSourceCode._evaluationIndex;
+        script.popSourceMapping(this._snippetScriptMapping);
+        uiSourceCode.scriptFile().setIsDivergingFromVM(false);
+        return script.rawLocationToUILocation(0, 0).uiSourceCode;
+    },
+
+    _debuggerReset: function()
+    {
+        for (var snippetId in this._uiSourceCodeForSnippetId) {
+            var uiSourceCode = this._uiSourceCodeForSnippetId[snippetId];
+            this._releaseSnippetScript(uiSourceCode);
+        }
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {string}
+     */
+    _evaluationSourceURL: function(uiSourceCode)
+    {
+        var evaluationSuffix = "_" + uiSourceCode._evaluationIndex;
+        var snippetId = this._snippetIdForUISourceCode.get(uiSourceCode);
+        return WebInspector.Script.snippetSourceURLPrefix + snippetId + evaluationSuffix;
+    },
+
+    /**
+     * @param {string} sourceURL
+     * @return {string|null}
+     */
+    _snippetIdForSourceURL: function(sourceURL)
+    {
+        var snippetPrefix = WebInspector.Script.snippetSourceURLPrefix;
+        if (!sourceURL.startsWith(snippetPrefix))
+            return null;
+        var splittedURL = sourceURL.substring(snippetPrefix.length).split("_");
+        var snippetId = splittedURL[0];
+        return snippetId;
+    },
+
+    reset: function()
+    {
+        /** @type {!Object.<string, WebInspector.UISourceCode>} */
+        this._uiSourceCodeForScriptId = {};
+        this._scriptForUISourceCode = new Map();
+        /** @type {!Object.<string, WebInspector.UISourceCode>} */
+        this._uiSourceCodeForSnippetId = {};
+        this._snippetIdForUISourceCode = new Map();
+        this._workspaceProvider.reset();
+        this._loadSnippets();
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ScriptFile}
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.ScriptSnippetModel} scriptSnippetModel
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ */
+WebInspector.SnippetScriptFile = function(scriptSnippetModel, uiSourceCode)
+{
+    WebInspector.ScriptFile.call(this);
+    this._scriptSnippetModel = scriptSnippetModel;
+    this._uiSourceCode = uiSourceCode;
+    this._hasDivergedFromVM = true;
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
+}
+
+WebInspector.SnippetScriptFile.prototype = {
+    /**
+     * @return {boolean}
+     */
+    hasDivergedFromVM: function()
+    {
+        return this._hasDivergedFromVM;
+    },
+
+    /**
+     * @param {boolean} hasDivergedFromVM
+     */
+    setHasDivergedFromVM: function(hasDivergedFromVM)
+    {
+        this._hasDivergedFromVM = hasDivergedFromVM;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isDivergingFromVM: function()
+    {
+        return this._isDivergingFromVM;
+    },
+
+    checkMapping: function()
+    {
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isMergingToVM: function()
+    {
+        return false;
+    },
+
+    /**
+     * @param {boolean} isDivergingFromVM
+     */
+    setIsDivergingFromVM: function(isDivergingFromVM)
+    {
+        this._isDivergingFromVM = isDivergingFromVM;
+    },
+
+    _workingCopyCommitted: function()
+    {
+        this._scriptSnippetModel._setScriptSnippetContent(this._uiSourceCode, this._uiSourceCode.workingCopy());
+    },
+
+    _workingCopyChanged: function()
+    {
+        this._scriptSnippetModel._scriptSnippetEdited(this._uiSourceCode);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ScriptSourceMapping}
+ * @param {WebInspector.ScriptSnippetModel} scriptSnippetModel
+ */
+WebInspector.SnippetScriptMapping = function(scriptSnippetModel)
+{
+    this._scriptSnippetModel = scriptSnippetModel;
+}
+
+WebInspector.SnippetScriptMapping.prototype = {
+    /**
+     * @param {WebInspector.RawLocation} rawLocation
+     * @return {WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(rawLocation)
+    {
+        var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */(rawLocation);
+        return this._scriptSnippetModel._rawLocationToUILocation(debuggerModelLocation);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.DebuggerModel.Location}
+     */
+    uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
+    {
+        return this._scriptSnippetModel._uiLocationToRawLocation(uiSourceCode, lineNumber, columnNumber);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isIdentity: function()
+    {
+        return true;
+    },
+
+    /**
+     * @param {string} sourceURL
+     * @return {string|null}
+     */
+    snippetIdForSourceURL: function(sourceURL)
+    {
+        return this._scriptSnippetModel._snippetIdForSourceURL(sourceURL);
+    },
+
+    /**
+     * @param {WebInspector.Script} script
+     */
+    addScript: function(script)
+    {
+        this._scriptSnippetModel._addScript(script);
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StaticContentProvider}
+ * @param {WebInspector.Snippet} snippet
+ */
+WebInspector.SnippetContentProvider = function(snippet)
+{
+    WebInspector.StaticContentProvider.call(this, WebInspector.resourceTypes.Script, snippet.content);
+}
+
+WebInspector.SnippetContentProvider.prototype = {
+    __proto__: WebInspector.StaticContentProvider.prototype
+}
+
+/**
+ * @type {?WebInspector.ScriptSnippetModel}
+ */
+WebInspector.scriptSnippetModel = null;
diff --git a/Source/devtools/front_end/ScriptsNavigator.js b/Source/devtools/front_end/ScriptsNavigator.js
new file mode 100644
index 0000000..8a2ff6f
--- /dev/null
+++ b/Source/devtools/front_end/ScriptsNavigator.js
@@ -0,0 +1,249 @@
+/*
+ * 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @extends {WebInspector.Object}
+ * @constructor
+ */
+WebInspector.ScriptsNavigator = function()
+{
+    WebInspector.Object.call(this);
+    
+    this._tabbedPane = new WebInspector.TabbedPane();
+    this._tabbedPane.shrinkableTabs = true;
+    this._tabbedPane.element.addStyleClass("navigator-tabbed-pane");
+
+    this._scriptsView = new WebInspector.NavigatorView();
+    this._scriptsView.addEventListener(WebInspector.NavigatorView.Events.ItemSelected, this._scriptSelected, this);
+    this._scriptsView.addEventListener(WebInspector.NavigatorView.Events.ItemSearchStarted, this._itemSearchStarted, this);
+
+    this._contentScriptsView = new WebInspector.NavigatorView();
+    this._contentScriptsView.addEventListener(WebInspector.NavigatorView.Events.ItemSelected, this._scriptSelected, this);
+    this._contentScriptsView.addEventListener(WebInspector.NavigatorView.Events.ItemSearchStarted, this._itemSearchStarted, this);
+
+    this._snippetsView = new WebInspector.SnippetsNavigatorView();
+    this._snippetsView.addEventListener(WebInspector.NavigatorView.Events.ItemSelected, this._scriptSelected, this);
+    this._snippetsView.addEventListener(WebInspector.NavigatorView.Events.ItemSearchStarted, this._itemSearchStarted, this);
+    this._snippetsView.addEventListener(WebInspector.NavigatorView.Events.FileRenamed, this._fileRenamed, this);
+    this._snippetsView.addEventListener(WebInspector.SnippetsNavigatorView.Events.SnippetCreationRequested, this._snippetCreationRequested, this);
+    this._snippetsView.addEventListener(WebInspector.SnippetsNavigatorView.Events.ItemRenamingRequested, this._itemRenamingRequested, this);
+
+    this._tabbedPane.appendTab(WebInspector.ScriptsNavigator.ScriptsTab, WebInspector.UIString("Sources"), this._scriptsView);
+    this._tabbedPane.selectTab(WebInspector.ScriptsNavigator.ScriptsTab);
+    this._tabbedPane.appendTab(WebInspector.ScriptsNavigator.ContentScriptsTab, WebInspector.UIString("Content scripts"), this._contentScriptsView);
+    this._tabbedPane.appendTab(WebInspector.ScriptsNavigator.SnippetsTab, WebInspector.UIString("Snippets"), this._snippetsView);
+}
+
+WebInspector.ScriptsNavigator.Events = {
+    ScriptSelected: "ScriptSelected",
+    SnippetCreationRequested: "SnippetCreationRequested",
+    ItemRenamingRequested: "ItemRenamingRequested",
+    ItemSearchStarted: "ItemSearchStarted",
+    FileRenamed: "FileRenamed"
+}
+
+WebInspector.ScriptsNavigator.ScriptsTab = "scripts";
+WebInspector.ScriptsNavigator.ContentScriptsTab = "contentScripts";
+WebInspector.ScriptsNavigator.SnippetsTab = "snippets";
+
+WebInspector.ScriptsNavigator.prototype = {
+    /*
+     * @return {WebInspector.View}
+     */
+    get view()
+    {
+        return this._tabbedPane;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _navigatorViewForUISourceCode: function(uiSourceCode)
+    {
+        if (uiSourceCode.isContentScript)
+            return this._contentScriptsView;
+        else if (uiSourceCode.project().type() === WebInspector.projectTypes.Snippets)
+            return this._snippetsView;
+        else
+            return this._scriptsView;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    addUISourceCode: function(uiSourceCode)
+    {
+        this._navigatorViewForUISourceCode(uiSourceCode).addUISourceCode(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    removeUISourceCode: function(uiSourceCode)
+    {
+        this._navigatorViewForUISourceCode(uiSourceCode).removeUISourceCode(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {boolean=} select
+     */
+    revealUISourceCode: function(uiSourceCode, select)
+    {
+        this._navigatorViewForUISourceCode(uiSourceCode).revealUISourceCode(uiSourceCode, select);
+        if (uiSourceCode.isContentScript)
+            this._tabbedPane.selectTab(WebInspector.ScriptsNavigator.ContentScriptsTab);
+        else if (uiSourceCode.project().type() !== WebInspector.projectTypes.Snippets)
+            this._tabbedPane.selectTab(WebInspector.ScriptsNavigator.ScriptsTab);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {function(boolean)=} callback
+     */
+    rename: function(uiSourceCode, callback)
+    {
+        this._navigatorViewForUISourceCode(uiSourceCode).rename(uiSourceCode, callback);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _scriptSelected: function(event)
+    {
+        this.dispatchEventToListeners(WebInspector.ScriptsNavigator.Events.ScriptSelected, event.data);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _itemSearchStarted: function(event)
+    {
+        this.dispatchEventToListeners(WebInspector.ScriptsNavigator.Events.ItemSearchStarted, event.data);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _fileRenamed: function(event)
+    {    
+        this.dispatchEventToListeners(WebInspector.ScriptsNavigator.Events.FileRenamed, event.data);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _itemRenamingRequested: function(event)
+    {
+        this.dispatchEventToListeners(WebInspector.ScriptsNavigator.Events.ItemRenamingRequested, event.data);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _snippetCreationRequested: function(event)
+    {    
+        this.dispatchEventToListeners(WebInspector.ScriptsNavigator.Events.SnippetCreationRequested, event.data);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.NavigatorView}
+ */
+WebInspector.SnippetsNavigatorView = function()
+{
+    WebInspector.NavigatorView.call(this);
+    this.element.addEventListener("contextmenu", this.handleContextMenu.bind(this), false);
+}
+
+WebInspector.SnippetsNavigatorView.Events = {
+    SnippetCreationRequested: "SnippetCreationRequested",
+    ItemRenamingRequested: "ItemRenamingRequested"
+}
+
+WebInspector.SnippetsNavigatorView.prototype = {
+    /**
+     * @param {Event} event
+     * @param {WebInspector.UISourceCode=} uiSourceCode
+     */
+    handleContextMenu: function(event, uiSourceCode)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        if (uiSourceCode) {
+            contextMenu.appendItem(WebInspector.UIString("Run"), this._handleEvaluateSnippet.bind(this, uiSourceCode));
+            contextMenu.appendItem(WebInspector.UIString("Rename"), this.handleRename.bind(this, uiSourceCode));
+            contextMenu.appendItem(WebInspector.UIString("Remove"), this._handleRemoveSnippet.bind(this, uiSourceCode));
+            contextMenu.appendSeparator();
+        }
+        contextMenu.appendItem(WebInspector.UIString("New"), this._handleCreateSnippet.bind(this));
+        contextMenu.show();
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _handleEvaluateSnippet: function(uiSourceCode)
+    {
+        if (uiSourceCode.project().type() !== WebInspector.projectTypes.Snippets)
+            return;
+        WebInspector.scriptSnippetModel.evaluateScriptSnippet(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    handleRename: function(uiSourceCode)
+    {
+        this.dispatchEventToListeners(WebInspector.ScriptsNavigator.Events.ItemRenamingRequested, uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _handleRemoveSnippet: function(uiSourceCode)
+    {
+        if (uiSourceCode.project().type() !== WebInspector.projectTypes.Snippets)
+            return;
+        WebInspector.scriptSnippetModel.deleteScriptSnippet(uiSourceCode);
+    },
+
+    _handleCreateSnippet: function()
+    {
+        this._snippetCreationRequested();
+    },
+
+    _snippetCreationRequested: function()
+    {
+        this.dispatchEventToListeners(WebInspector.SnippetsNavigatorView.Events.SnippetCreationRequested, null);
+    },
+
+    __proto__: WebInspector.NavigatorView.prototype
+}
diff --git a/Source/devtools/front_end/ScriptsPanel.js b/Source/devtools/front_end/ScriptsPanel.js
new file mode 100755
index 0000000..3237e67
--- /dev/null
+++ b/Source/devtools/front_end/ScriptsPanel.js
@@ -0,0 +1,1329 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+importScript("BreakpointsSidebarPane.js");
+importScript("CallStackSidebarPane.js");
+importScript("FilteredItemSelectionDialog.js");
+importScript("UISourceCodeFrame.js");
+importScript("JavaScriptSourceFrame.js");
+importScript("NavigatorOverlayController.js");
+importScript("NavigatorView.js");
+importScript("RevisionHistoryView.js");
+importScript("ScopeChainSidebarPane.js");
+importScript("ScriptsNavigator.js");
+importScript("ScriptsSearchScope.js");
+importScript("StyleSheetOutlineDialog.js");
+importScript("TabbedEditorContainer.js");
+importScript("WatchExpressionsSidebarPane.js");
+importScript("WorkersSidebarPane.js");
+
+/**
+ * @constructor
+ * @implements {WebInspector.TabbedEditorContainerDelegate}
+ * @implements {WebInspector.ContextMenu.Provider}
+ * @extends {WebInspector.Panel}
+ * @param {WebInspector.Workspace=} workspaceForTest
+ */
+WebInspector.ScriptsPanel = function(workspaceForTest)
+{
+    WebInspector.Panel.call(this, "scripts");
+    this.registerRequiredCSS("scriptsPanel.css");
+
+    WebInspector.settings.navigatorWasOnceHidden = WebInspector.settings.createSetting("navigatorWasOnceHidden", false);
+    WebInspector.settings.debuggerSidebarHidden = WebInspector.settings.createSetting("debuggerSidebarHidden", false);
+
+    this._workspace = workspaceForTest || WebInspector.workspace;
+
+    function viewGetter()
+    {
+        return this.visibleView;
+    }
+    WebInspector.GoToLineDialog.install(this, viewGetter.bind(this));
+
+    var helpSection = WebInspector.shortcutsScreen.section(WebInspector.UIString("Sources Panel"));
+    this.debugToolbar = this._createDebugToolbar();
+
+    const initialDebugSidebarWidth = 225;
+    const minimumDebugSidebarWidthPercent = 50;
+    this.createSidebarView(this.element, WebInspector.SidebarView.SidebarPosition.End, initialDebugSidebarWidth);
+    this.splitView.element.id = "scripts-split-view";
+    this.splitView.setMinimumSidebarWidth(Preferences.minScriptsSidebarWidth);
+    this.splitView.setMinimumMainWidthPercent(minimumDebugSidebarWidthPercent);
+
+    this.debugSidebarResizeWidgetElement = document.createElement("div");
+    this.debugSidebarResizeWidgetElement.id = "scripts-debug-sidebar-resizer-widget";
+    this.splitView.installResizer(this.debugSidebarResizeWidgetElement);
+
+    // Create scripts navigator
+    const initialNavigatorWidth = 225;
+    const minimumViewsContainerWidthPercent = 50;
+    this.editorView = new WebInspector.SidebarView(WebInspector.SidebarView.SidebarPosition.Start, "scriptsPanelNavigatorSidebarWidth", initialNavigatorWidth);
+    this.editorView.element.tabIndex = 0;
+
+    this.editorView.setMinimumSidebarWidth(Preferences.minScriptsSidebarWidth);
+    this.editorView.setMinimumMainWidthPercent(minimumViewsContainerWidthPercent);
+    this.editorView.show(this.splitView.mainElement);
+
+    this._navigator = new WebInspector.ScriptsNavigator();
+    this._navigator.view.show(this.editorView.sidebarElement);
+
+    this._editorContainer = new WebInspector.TabbedEditorContainer(this, "previouslyViewedFiles");
+    this._editorContainer.show(this.editorView.mainElement);
+
+    this._navigatorController = new WebInspector.NavigatorOverlayController(this.editorView, this._navigator.view, this._editorContainer.view);
+
+    this._navigator.addEventListener(WebInspector.ScriptsNavigator.Events.ScriptSelected, this._scriptSelected, this);
+    this._navigator.addEventListener(WebInspector.ScriptsNavigator.Events.ItemSearchStarted, this._itemSearchStarted, this);
+    this._navigator.addEventListener(WebInspector.ScriptsNavigator.Events.SnippetCreationRequested, this._snippetCreationRequested, this);
+    this._navigator.addEventListener(WebInspector.ScriptsNavigator.Events.ItemRenamingRequested, this._itemRenamingRequested, this);
+    this._navigator.addEventListener(WebInspector.ScriptsNavigator.Events.FileRenamed, this._fileRenamed, this);
+
+    this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorSelected, this._editorSelected, this);
+    this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorClosed, this._editorClosed, this);
+
+    this.splitView.mainElement.appendChild(this.debugSidebarResizeWidgetElement);
+
+    this.sidebarPanes = {};
+    this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane();
+    this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
+    this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
+    this.sidebarPanes.jsBreakpoints = new WebInspector.JavaScriptBreakpointsSidebarPane(WebInspector.breakpointManager, this._showSourceLine.bind(this));
+    this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane.createProxy(this);
+    this.sidebarPanes.xhrBreakpoints = new WebInspector.XHRBreakpointsSidebarPane();
+    this.sidebarPanes.eventListenerBreakpoints = new WebInspector.EventListenerBreakpointsSidebarPane();
+
+    if (Capabilities.canInspectWorkers && !WebInspector.WorkerManager.isWorkerFrontend()) {
+        WorkerAgent.enable();
+        this.sidebarPanes.workerList = new WebInspector.WorkersSidebarPane(WebInspector.workerManager);
+    }
+
+    this.sidebarPanes.callstack.registerShortcuts(this.registerShortcuts.bind(this));
+    this.registerShortcuts(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.EvaluateSelectionInConsole, this._evaluateSelectionInConsole.bind(this));
+    this.registerShortcuts(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.GoToMember, this._showOutlineDialog.bind(this));
+    this.registerShortcuts(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.ToggleBreakpoint, this._toggleBreakpoint.bind(this));
+
+    this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3);
+    this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions, this);
+
+    this._toggleFormatSourceButton = new WebInspector.StatusBarButton(WebInspector.UIString("Pretty print"), "scripts-toggle-pretty-print-status-bar-item");
+    this._toggleFormatSourceButton.toggled = false;
+    this._toggleFormatSourceButton.addEventListener("click", this._toggleFormatSource, this);
+
+    this._scriptViewStatusBarItemsContainer = document.createElement("div");
+    this._scriptViewStatusBarItemsContainer.className = "inline-block";
+
+    this._scriptViewStatusBarTextContainer = document.createElement("div");
+    this._scriptViewStatusBarTextContainer.className = "inline-block";
+
+    this._installDebuggerSidebarController();
+
+    WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._dockSideChanged.bind(this));
+    WebInspector.settings.splitVerticallyWhenDockedToRight.addChangeListener(this._dockSideChanged.bind(this));
+    this._dockSideChanged();
+
+    this._sourceFramesByUISourceCode = new Map();
+    this._updateDebuggerButtons();
+    this._pauseOnExceptionStateChanged();
+    if (WebInspector.debuggerModel.isPaused())
+        this._debuggerPaused();
+
+    WebInspector.settings.pauseOnExceptionStateString.addChangeListener(this._pauseOnExceptionStateChanged, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasDisabled, this._debuggerWasDisabled, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.CallFrameSelected, this._callFrameSelected, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame, this._consoleCommandEvaluatedInSelectedCallFrame, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ExecutionLineChanged, this._executionLineChanged, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointsActiveStateChanged, this._breakpointsActiveStateChanged, this);
+
+    WebInspector.startBatchUpdate();
+    var uiSourceCodes = this._workspace.uiSourceCodes();
+    for (var i = 0; i < uiSourceCodes.length; ++i)
+        this._addUISourceCode(uiSourceCodes[i]);
+    WebInspector.endBatchUpdate();
+
+    this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
+    this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
+    this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset.bind(this), this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
+
+    WebInspector.advancedSearchController.registerSearchScope(new WebInspector.ScriptsSearchScope(this._workspace));
+}
+
+WebInspector.ScriptsPanel.prototype = {
+    get statusBarItems()
+    {
+        return [this._pauseOnExceptionButton.element, this._toggleFormatSourceButton.element, this._scriptViewStatusBarItemsContainer];
+    },
+
+    /**
+     * @return {?Element}
+     */
+    statusBarText: function()
+    {
+        return this._scriptViewStatusBarTextContainer;
+    },
+
+    defaultFocusedElement: function()
+    {
+        return this._editorContainer.view.defaultFocusedElement() || this._navigator.view.defaultFocusedElement();
+    },
+
+    get paused()
+    {
+        return this._paused;
+    },
+
+    wasShown: function()
+    {
+        WebInspector.Panel.prototype.wasShown.call(this);
+        this._navigatorController.wasShown();
+    },
+
+    willHide: function()
+    {
+        WebInspector.Panel.prototype.willHide.call(this);
+        WebInspector.closeViewInDrawer();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _uiSourceCodeAdded: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        this._addUISourceCode(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _addUISourceCode: function(uiSourceCode)
+    {
+        if (this._toggleFormatSourceButton.toggled)
+            uiSourceCode.setFormatted(true);
+        if (uiSourceCode.project().isServiceProject())
+            return;
+        this._navigator.addUISourceCode(uiSourceCode);
+        this._editorContainer.addUISourceCode(uiSourceCode);
+        // Replace debugger script-based uiSourceCode with a network-based one.
+        var currentUISourceCode = this._currentUISourceCode;
+        if (currentUISourceCode && currentUISourceCode.project().isServiceProject() && currentUISourceCode !== uiSourceCode && currentUISourceCode.url === uiSourceCode.url) {
+            this._showFile(uiSourceCode);
+            this._editorContainer.removeUISourceCode(currentUISourceCode);
+        }
+    },
+
+    _uiSourceCodeRemoved: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        this._removeUISourceCodes([uiSourceCode]);
+    },
+
+    /**
+     * @param {Array.<WebInspector.UISourceCode>} uiSourceCodes
+     */
+    _removeUISourceCodes: function(uiSourceCodes)
+    {
+        for (var i = 0; i < uiSourceCodes.length; ++i) {
+            this._navigator.removeUISourceCode(uiSourceCodes[i]);
+            this._removeSourceFrame(uiSourceCodes[i]);
+        }
+        this._editorContainer.removeUISourceCodes(uiSourceCodes);
+    },
+
+    _consoleCommandEvaluatedInSelectedCallFrame: function(event)
+    {
+        this.sidebarPanes.scopechain.update(WebInspector.debuggerModel.selectedCallFrame());
+    },
+
+    _debuggerPaused: function()
+    {
+        var details = WebInspector.debuggerModel.debuggerPausedDetails();
+
+        this._paused = true;
+        this._waitingToPause = false;
+        this._stepping = false;
+
+        this._updateDebuggerButtons();
+
+        WebInspector.inspectorView.setCurrentPanel(this);
+        this.sidebarPanes.callstack.update(details.callFrames);
+
+        if (details.reason === WebInspector.DebuggerModel.BreakReason.DOM) {
+            WebInspector.domBreakpointsSidebarPane.highlightBreakpoint(details.auxData);
+            function didCreateBreakpointHitStatusMessage(element)
+            {
+                this.sidebarPanes.callstack.setStatus(element);
+            }
+            WebInspector.domBreakpointsSidebarPane.createBreakpointHitStatusMessage(details.auxData, didCreateBreakpointHitStatusMessage.bind(this));
+        } else if (details.reason === WebInspector.DebuggerModel.BreakReason.EventListener) {
+            var eventName = details.auxData.eventName;
+            this.sidebarPanes.eventListenerBreakpoints.highlightBreakpoint(details.auxData.eventName);
+            var eventNameForUI = WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI(eventName);
+            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a \"%s\" Event Listener.", eventNameForUI));
+        } else if (details.reason === WebInspector.DebuggerModel.BreakReason.XHR) {
+            this.sidebarPanes.xhrBreakpoints.highlightBreakpoint(details.auxData["breakpointURL"]);
+            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a XMLHttpRequest."));
+        } else if (details.reason === WebInspector.DebuggerModel.BreakReason.Exception)
+            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on exception: '%s'.", details.auxData.description));
+        else if (details.reason === WebInspector.DebuggerModel.BreakReason.Assert)
+            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on assertion."));
+        else if (details.reason === WebInspector.DebuggerModel.BreakReason.CSPViolation)
+            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a script blocked due to Content Security Policy directive: \"%s\".", details.auxData["directiveText"]));
+        else {
+            function didGetUILocation(uiLocation)
+            {
+                var breakpoint = WebInspector.breakpointManager.findBreakpoint(uiLocation.uiSourceCode, uiLocation.lineNumber);
+                if (!breakpoint)
+                    return;
+                this.sidebarPanes.jsBreakpoints.highlightBreakpoint(breakpoint);
+                this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a JavaScript breakpoint."));
+            }
+            if (details.callFrames.length) 
+                details.callFrames[0].createLiveLocation(didGetUILocation.bind(this));
+            else
+                console.warn("ScriptsPanel paused, but callFrames.length is zero."); // TODO remove this once we understand this case better
+        }
+
+        this._showDebuggerSidebar();
+        this._toggleDebuggerSidebarButton.setEnabled(false);
+        window.focus();
+        InspectorFrontendHost.bringToFront();
+    },
+
+    _debuggerResumed: function()
+    {
+        this._paused = false;
+        this._waitingToPause = false;
+        this._stepping = false;
+
+        this._clearInterface();
+        this._toggleDebuggerSidebarButton.setEnabled(true);
+    },
+
+    _debuggerWasEnabled: function()
+    {
+        this._updateDebuggerButtons();
+    },
+
+    _debuggerWasDisabled: function()
+    {
+        this._debuggerReset();
+    },
+
+    _debuggerReset: function()
+    {
+        this._debuggerResumed();
+        this.sidebarPanes.watchExpressions.reset();
+    },
+
+    _projectWillReset: function(event)
+    {
+        var project = event.data;
+        var uiSourceCodes = project.uiSourceCodes();
+        this._removeUISourceCodes(uiSourceCodes);
+        if (project.type() === WebInspector.projectTypes.Network)
+            this._editorContainer.reset();
+    },
+
+    get visibleView()
+    {
+        return this._editorContainer.visibleView;
+    },
+
+    _updateScriptViewStatusBarItems: function()
+    {
+        this._scriptViewStatusBarItemsContainer.removeChildren();
+        this._scriptViewStatusBarTextContainer.removeChildren();
+
+        var sourceFrame = this.visibleView;
+        if (sourceFrame) {
+            var statusBarItems = sourceFrame.statusBarItems() || [];
+            for (var i = 0; i < statusBarItems.length; ++i)
+                this._scriptViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
+            var statusBarText = sourceFrame.statusBarText();
+            if (statusBarText)
+                this._scriptViewStatusBarTextContainer.appendChild(statusBarText);
+        }
+    },
+
+    canShowAnchorLocation: function(anchor)
+    {
+        if (WebInspector.debuggerModel.debuggerEnabled() && anchor.uiSourceCode)
+            return true;
+        var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(anchor.href);
+        if (uiSourceCode) {
+            anchor.uiSourceCode = uiSourceCode;
+            return true;
+        }
+        return false;
+    },
+
+    showAnchorLocation: function(anchor)
+    {
+        this._showSourceLine(anchor.uiSourceCode, anchor.lineNumber);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number=} lineNumber
+     */
+    showUISourceCode: function(uiSourceCode, lineNumber)
+    {
+        this._showSourceLine(uiSourceCode, lineNumber);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number=} lineNumber
+     */
+    _showSourceLine: function(uiSourceCode, lineNumber)
+    {
+        var sourceFrame = this._showFile(uiSourceCode);
+        if (typeof lineNumber === "number")
+            sourceFrame.highlightLine(lineNumber);
+        sourceFrame.focus();
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.OpenSourceLink,
+            url: uiSourceCode.originURL(),
+            lineNumber: lineNumber
+        });
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {WebInspector.SourceFrame}
+     */
+    _showFile: function(uiSourceCode)
+    {
+        var sourceFrame = this._getOrCreateSourceFrame(uiSourceCode);
+        if (this._currentUISourceCode === uiSourceCode)
+            return sourceFrame;
+        this._currentUISourceCode = uiSourceCode;
+        if (!uiSourceCode.project().isServiceProject())
+            this._navigator.revealUISourceCode(uiSourceCode, true);
+        this._editorContainer.showFile(uiSourceCode);
+        this._updateScriptViewStatusBarItems();
+
+        if (this._currentUISourceCode.project().type() === WebInspector.projectTypes.Snippets)
+            this._runSnippetButton.element.removeStyleClass("hidden");
+        else
+            this._runSnippetButton.element.addStyleClass("hidden");
+
+        return sourceFrame;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {WebInspector.SourceFrame}
+     */
+    _createSourceFrame: function(uiSourceCode)
+    {
+        var sourceFrame;
+        switch (uiSourceCode.contentType()) {
+        case WebInspector.resourceTypes.Script:
+            sourceFrame = new WebInspector.JavaScriptSourceFrame(this, uiSourceCode);
+            break;
+        case WebInspector.resourceTypes.Document:
+            sourceFrame = new WebInspector.JavaScriptSourceFrame(this, uiSourceCode);
+            break;
+        case WebInspector.resourceTypes.Stylesheet:
+        default:
+            sourceFrame = new WebInspector.UISourceCodeFrame(uiSourceCode);
+        break;
+        }
+        this._sourceFramesByUISourceCode.put(uiSourceCode, sourceFrame);
+        return sourceFrame;
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {WebInspector.SourceFrame}
+     */
+    _getOrCreateSourceFrame: function(uiSourceCode)
+    {
+        return this._sourceFramesByUISourceCode.get(uiSourceCode) || this._createSourceFrame(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {WebInspector.SourceFrame}
+     */
+    viewForFile: function(uiSourceCode)
+    {
+        return this._getOrCreateSourceFrame(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _removeSourceFrame: function(uiSourceCode)
+    {
+        var sourceFrame = this._sourceFramesByUISourceCode.get(uiSourceCode);
+        if (!sourceFrame)
+            return;
+        this._sourceFramesByUISourceCode.remove(uiSourceCode);
+        sourceFrame.dispose();
+    },
+
+    _clearCurrentExecutionLine: function()
+    {
+        if (this._executionSourceFrame)
+            this._executionSourceFrame.clearExecutionLine();
+        delete this._executionSourceFrame;
+    },
+
+    _executionLineChanged: function(event)
+    {
+        var uiLocation = event.data;
+
+        this._clearCurrentExecutionLine();
+        if (!uiLocation)
+            return;
+        var sourceFrame = this._getOrCreateSourceFrame(uiLocation.uiSourceCode);
+        sourceFrame.setExecutionLine(uiLocation.lineNumber);
+        this._executionSourceFrame = sourceFrame;
+    },
+
+    _revealExecutionLine: function(uiLocation)
+    {
+        var uiSourceCode = uiLocation.uiSourceCode;
+        // Some scripts (anonymous and snippets evaluations) are not added to files select by default.
+        if (this._currentUISourceCode && this._currentUISourceCode.scriptFile() && this._currentUISourceCode.scriptFile().isDivergingFromVM())
+            return;
+        if (this._toggleFormatSourceButton.toggled && !uiSourceCode.formatted())
+            uiSourceCode.setFormatted(true);
+        var sourceFrame = this._showFile(uiSourceCode);
+        sourceFrame.revealLine(uiLocation.lineNumber);
+        sourceFrame.focus();
+    },
+
+    _callFrameSelected: function(event)
+    {
+        var callFrame = event.data;
+
+        if (!callFrame)
+            return;
+
+        this.sidebarPanes.scopechain.update(callFrame);
+        this.sidebarPanes.watchExpressions.refreshExpressions();
+        this.sidebarPanes.callstack.setSelectedCallFrame(callFrame);
+        callFrame.createLiveLocation(this._revealExecutionLine.bind(this));
+    },
+
+    _editorClosed: function(event)
+    {
+        this._navigatorController.hideNavigatorOverlay();
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+
+        if (this._currentUISourceCode === uiSourceCode)
+            delete this._currentUISourceCode;
+
+        // ScriptsNavigator does not need to update on EditorClosed.
+        this._updateScriptViewStatusBarItems();
+        WebInspector.searchController.resetSearch();
+    },
+
+    _editorSelected: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        var sourceFrame = this._showFile(uiSourceCode);
+        this._navigatorController.hideNavigatorOverlay();
+        if (!this._navigatorController.isNavigatorPinned())
+            sourceFrame.focus();
+        WebInspector.searchController.resetSearch();
+    },
+
+    _scriptSelected: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode);
+        var sourceFrame = this._showFile(uiSourceCode);
+        this._navigatorController.hideNavigatorOverlay();
+        if (sourceFrame && (!this._navigatorController.isNavigatorPinned() || event.data.focusSource))
+            sourceFrame.focus();
+    },
+
+    _itemSearchStarted: function(event)
+    {
+        var searchText = /** @type {string} */ (event.data);
+        WebInspector.OpenResourceDialog.show(this, this.editorView.mainElement, searchText);
+    },
+
+    _pauseOnExceptionStateChanged: function()
+    {
+        var pauseOnExceptionsState = WebInspector.settings.pauseOnExceptionStateString.get();
+        switch (pauseOnExceptionsState) {
+        case WebInspector.DebuggerModel.PauseOnExceptionsState.DontPauseOnExceptions:
+            this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions.");
+            break;
+        case WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnAllExceptions:
+            this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions.");
+            break;
+        case WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnUncaughtExceptions:
+            this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions.");
+            break;
+        }
+        this._pauseOnExceptionButton.state = pauseOnExceptionsState;
+    },
+
+    _updateDebuggerButtons: function()
+    {
+        if (WebInspector.debuggerModel.debuggerEnabled()) {
+            this._pauseOnExceptionButton.visible = true;
+        } else {
+            this._pauseOnExceptionButton.visible = false;
+        }
+
+        if (this._paused) {
+            this._updateButtonTitle(this._pauseButton, WebInspector.UIString("Resume script execution (%s)."))
+            this._pauseButton.state = true;
+
+            this._pauseButton.setEnabled(true);
+            this._stepOverButton.setEnabled(true);
+            this._stepIntoButton.setEnabled(true);
+            this._stepOutButton.setEnabled(true);
+
+            this.debuggerStatusElement.textContent = WebInspector.UIString("Paused");
+        } else {
+            this._updateButtonTitle(this._pauseButton, WebInspector.UIString("Pause script execution (%s)."))
+            this._pauseButton.state = false;
+
+            this._pauseButton.setEnabled(!this._waitingToPause);
+            this._stepOverButton.setEnabled(false);
+            this._stepIntoButton.setEnabled(false);
+            this._stepOutButton.setEnabled(false);
+
+            if (this._waitingToPause)
+                this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing");
+            else if (this._stepping)
+                this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping");
+            else
+                this.debuggerStatusElement.textContent = "";
+        }
+    },
+
+    _clearInterface: function()
+    {
+        this.sidebarPanes.callstack.update(null);
+        this.sidebarPanes.scopechain.update(null);
+        this.sidebarPanes.jsBreakpoints.clearBreakpointHighlight();
+        WebInspector.domBreakpointsSidebarPane.clearBreakpointHighlight();
+        this.sidebarPanes.eventListenerBreakpoints.clearBreakpointHighlight();
+        this.sidebarPanes.xhrBreakpoints.clearBreakpointHighlight();
+
+        this._clearCurrentExecutionLine();
+        this._updateDebuggerButtons();
+    },
+
+    _togglePauseOnExceptions: function()
+    {
+        var nextStateMap = {};
+        var stateEnum = WebInspector.DebuggerModel.PauseOnExceptionsState;
+        nextStateMap[stateEnum.DontPauseOnExceptions] = stateEnum.PauseOnAllExceptions;
+        nextStateMap[stateEnum.PauseOnAllExceptions] = stateEnum.PauseOnUncaughtExceptions;
+        nextStateMap[stateEnum.PauseOnUncaughtExceptions] = stateEnum.DontPauseOnExceptions;
+        WebInspector.settings.pauseOnExceptionStateString.set(nextStateMap[this._pauseOnExceptionButton.state]);
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _runSnippet: function(event)
+    {
+        if (this._currentUISourceCode.project().type() !== WebInspector.projectTypes.Snippets)
+            return false;
+        WebInspector.scriptSnippetModel.evaluateScriptSnippet(this._currentUISourceCode);
+        return true;
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _togglePause: function(event)
+    {
+        if (this._paused) {
+            this._paused = false;
+            this._waitingToPause = false;
+            DebuggerAgent.resume();
+        } else {
+            this._stepping = false;
+            this._waitingToPause = true;
+            DebuggerAgent.pause();
+        }
+
+        this._clearInterface();
+        return true;
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _stepOverClicked: function(event)
+    {
+        if (!this._paused)
+            return true;
+
+        this._paused = false;
+        this._stepping = true;
+
+        this._clearInterface();
+
+        DebuggerAgent.stepOver();
+        return true;
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _stepIntoClicked: function(event)
+    {
+        if (!this._paused)
+            return true;
+
+        this._paused = false;
+        this._stepping = true;
+
+        this._clearInterface();
+
+        DebuggerAgent.stepInto();
+        return true;
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _stepOutClicked: function(event)
+    {
+        if (!this._paused)
+            return true;
+
+        this._paused = false;
+        this._stepping = true;
+
+        this._clearInterface();
+
+        DebuggerAgent.stepOut();
+        return true;
+    },
+
+    _toggleBreakpointsClicked: function(event)
+    {
+        WebInspector.debuggerModel.setBreakpointsActive(!WebInspector.debuggerModel.breakpointsActive());
+    },
+
+    _breakpointsActiveStateChanged: function(event)
+    {
+        var active = event.data;
+        this._toggleBreakpointsButton.toggled = !active;
+        if (active) {
+            this._toggleBreakpointsButton.title = WebInspector.UIString("Deactivate breakpoints.");
+            WebInspector.inspectorView.element.removeStyleClass("breakpoints-deactivated");
+            this.sidebarPanes.jsBreakpoints.listElement.removeStyleClass("breakpoints-list-deactivated");
+        } else {
+            this._toggleBreakpointsButton.title = WebInspector.UIString("Activate breakpoints.");
+            WebInspector.inspectorView.element.addStyleClass("breakpoints-deactivated");
+            this.sidebarPanes.jsBreakpoints.listElement.addStyleClass("breakpoints-list-deactivated");
+        }
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _evaluateSelectionInConsole: function(event)
+    {
+        var selection = window.getSelection();
+        if (selection.type !== "Range" || selection.isCollapsed)
+            return false;
+        WebInspector.evaluateInConsole(selection.toString());
+        return true;
+    },
+
+    _createDebugToolbar: function()
+    {
+        var debugToolbar = document.createElement("div");
+        debugToolbar.className = "status-bar";
+        debugToolbar.id = "scripts-debug-toolbar";
+
+        var title, handler;
+        var platformSpecificModifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta;
+
+        // Run snippet.
+        title = WebInspector.UIString("Run snippet (%s).");
+        handler = this._runSnippet.bind(this);
+        this._runSnippetButton = this._createButtonAndRegisterShortcuts("scripts-run-snippet", title, handler, WebInspector.ScriptsPanelDescriptor.ShortcutKeys.RunSnippet);
+        debugToolbar.appendChild(this._runSnippetButton.element);
+        this._runSnippetButton.element.addStyleClass("hidden");
+
+        // Continue.
+        handler = this._togglePause.bind(this);
+        this._pauseButton = this._createButtonAndRegisterShortcuts("scripts-pause", "", handler, WebInspector.ScriptsPanelDescriptor.ShortcutKeys.PauseContinue);
+        debugToolbar.appendChild(this._pauseButton.element);
+
+        // Step over.
+        title = WebInspector.UIString("Step over next function call (%s).");
+        handler = this._stepOverClicked.bind(this);
+        this._stepOverButton = this._createButtonAndRegisterShortcuts("scripts-step-over", title, handler, WebInspector.ScriptsPanelDescriptor.ShortcutKeys.StepOver);
+        debugToolbar.appendChild(this._stepOverButton.element);
+
+        // Step into.
+        title = WebInspector.UIString("Step into next function call (%s).");
+        handler = this._stepIntoClicked.bind(this);
+        this._stepIntoButton = this._createButtonAndRegisterShortcuts("scripts-step-into", title, handler, WebInspector.ScriptsPanelDescriptor.ShortcutKeys.StepInto);
+        debugToolbar.appendChild(this._stepIntoButton.element);
+
+        // Step out.
+        title = WebInspector.UIString("Step out of current function (%s).");
+        handler = this._stepOutClicked.bind(this);
+        this._stepOutButton = this._createButtonAndRegisterShortcuts("scripts-step-out", title, handler, WebInspector.ScriptsPanelDescriptor.ShortcutKeys.StepOut);
+        debugToolbar.appendChild(this._stepOutButton.element);
+
+        this._toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate breakpoints."), "scripts-toggle-breakpoints");
+        this._toggleBreakpointsButton.toggled = false;
+        this._toggleBreakpointsButton.addEventListener("click", this._toggleBreakpointsClicked, this);
+        debugToolbar.appendChild(this._toggleBreakpointsButton.element);
+
+        this.debuggerStatusElement = document.createElement("div");
+        this.debuggerStatusElement.id = "scripts-debugger-status";
+        debugToolbar.appendChild(this.debuggerStatusElement);
+
+        return debugToolbar;
+    },
+
+    /**
+     * @param {WebInspector.StatusBarButton} button
+     * @param {string} buttonTitle
+     */
+    _updateButtonTitle: function(button, buttonTitle)
+    {
+        var hasShortcuts = button.shortcuts && button.shortcuts.length;
+        if (hasShortcuts)
+            button.title = String.vsprintf(buttonTitle, [button.shortcuts[0].name]);
+        else
+            button.title = buttonTitle;
+    },
+
+    /**
+     * @param {string} buttonId
+     * @param {string} buttonTitle
+     * @param {function(Event=):boolean} handler
+     * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} shortcuts
+     * @return {WebInspector.StatusBarButton}
+     */
+    _createButtonAndRegisterShortcuts: function(buttonId, buttonTitle, handler, shortcuts)
+    {
+        var button = new WebInspector.StatusBarButton(buttonTitle, buttonId);
+        button.element.addEventListener("click", handler, false);
+        button.shortcuts = shortcuts;
+        this._updateButtonTitle(button, buttonTitle);
+        this.registerShortcuts(shortcuts, handler);
+        return button;
+    },
+
+    searchCanceled: function()
+    {
+        if (this._searchView)
+            this._searchView.searchCanceled();
+
+        delete this._searchView;
+        delete this._searchQuery;
+    },
+
+    /**
+     * @param {string} query
+     */
+    performSearch: function(query)
+    {
+        WebInspector.searchController.updateSearchMatchesCount(0, this);
+
+        if (!this.visibleView)
+            return;
+
+        // Call searchCanceled since it will reset everything we need before doing a new search.
+        this.searchCanceled();
+
+        this._searchView = this.visibleView;
+        this._searchQuery = query;
+
+        function finishedCallback(view, searchMatches)
+        {
+            if (!searchMatches)
+                return;
+
+            WebInspector.searchController.updateSearchMatchesCount(searchMatches, this);
+            view.jumpToNextSearchResult();
+            WebInspector.searchController.updateCurrentMatchIndex(view.currentSearchResultIndex, this);
+        }
+
+        this._searchView.performSearch(query, finishedCallback.bind(this));
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        if (!this._searchView)
+            return;
+
+        if (this._searchView !== this.visibleView) {
+            this.performSearch(this._searchQuery);
+            return;
+        }
+
+        if (this._searchView.showingLastSearchResult())
+            this._searchView.jumpToFirstSearchResult();
+        else
+            this._searchView.jumpToNextSearchResult();
+        WebInspector.searchController.updateCurrentMatchIndex(this._searchView.currentSearchResultIndex, this);
+        return true;
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        if (!this._searchView)
+            return;
+
+        if (this._searchView !== this.visibleView) {
+            this.performSearch(this._searchQuery);
+            if (this._searchView)
+                this._searchView.jumpToLastSearchResult();
+            return;
+        }
+
+        if (this._searchView.showingFirstSearchResult())
+            this._searchView.jumpToLastSearchResult();
+        else
+            this._searchView.jumpToPreviousSearchResult();
+        WebInspector.searchController.updateCurrentMatchIndex(this._searchView.currentSearchResultIndex, this);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canSearchAndReplace: function()
+    {
+        var view = /** @type {WebInspector.SourceFrame} */ (this.visibleView);
+        return !!view && view.canEditSource();
+    },
+
+    /**
+     * @param {string} text
+     */
+    replaceSelectionWith: function(text)
+    {
+        var view = /** @type {WebInspector.SourceFrame} */ (this.visibleView);
+        view.replaceSearchMatchWith(text);
+    },
+
+    /**
+     * @param {string} query
+     * @param {string} text
+     */
+    replaceAllWith: function(query, text)
+    {
+        var view = /** @type {WebInspector.SourceFrame} */ (this.visibleView);
+        view.replaceAllWith(query, text);
+    },
+
+    _toggleFormatSource: function()
+    {
+        this._toggleFormatSourceButton.toggled = !this._toggleFormatSourceButton.toggled;
+        var uiSourceCodes = this._workspace.uiSourceCodes();
+        for (var i = 0; i < uiSourceCodes.length; ++i)
+            uiSourceCodes[i].setFormatted(this._toggleFormatSourceButton.toggled);
+
+        var currentFile = this._editorContainer.currentFile();
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.TogglePrettyPrint,
+            enabled: this._toggleFormatSourceButton.toggled,
+            url: currentFile ? currentFile.originURL() : null
+        });
+    },
+
+    addToWatch: function(expression)
+    {
+        this.sidebarPanes.watchExpressions.addExpression(expression);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _toggleBreakpoint: function()
+    {
+        var sourceFrame = this.visibleView;
+        if (!sourceFrame)
+            return false;
+
+        if (sourceFrame instanceof WebInspector.JavaScriptSourceFrame) {
+            var javaScriptSourceFrame = /** @type {WebInspector.JavaScriptSourceFrame} */ (sourceFrame);
+            javaScriptSourceFrame.toggleBreakpointOnCurrentLine();
+            return true;
+        }
+        return false;
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _showOutlineDialog: function(event)
+    {
+        var uiSourceCode = this._editorContainer.currentFile();
+        if (!uiSourceCode)
+            return false;
+
+        switch (uiSourceCode.contentType()) {
+        case WebInspector.resourceTypes.Document:
+        case WebInspector.resourceTypes.Script:
+            WebInspector.JavaScriptOutlineDialog.show(this.visibleView, uiSourceCode);
+            return true;
+        case WebInspector.resourceTypes.Stylesheet:
+            WebInspector.StyleSheetOutlineDialog.show(this.visibleView, uiSourceCode);
+            return true;
+        }
+        return false;
+    },
+
+    _installDebuggerSidebarController: function()
+    {
+        this._toggleDebuggerSidebarButton = new WebInspector.StatusBarButton(WebInspector.UIString("Hide debugger"), "scripts-debugger-show-hide-button", 3);
+        this._toggleDebuggerSidebarButton.state = "shown";
+        this._toggleDebuggerSidebarButton.addEventListener("click", clickHandler, this);
+
+        function clickHandler()
+        {
+            if (this._toggleDebuggerSidebarButton.state === "shown")
+                this._hideDebuggerSidebar();
+            else
+                this._showDebuggerSidebar();
+        }
+        this.editorView.element.appendChild(this._toggleDebuggerSidebarButton.element);
+
+        if (WebInspector.settings.debuggerSidebarHidden.get())
+            this._hideDebuggerSidebar();
+
+    },
+
+    _showDebuggerSidebar: function()
+    {
+        if (this._toggleDebuggerSidebarButton.state === "shown")
+            return;
+        this._toggleDebuggerSidebarButton.state = "shown";
+        this._toggleDebuggerSidebarButton.title = WebInspector.UIString("Hide debugger");
+        this.splitView.showSidebarElement();
+        this.debugSidebarResizeWidgetElement.removeStyleClass("hidden");
+        WebInspector.settings.debuggerSidebarHidden.set(false);
+    },
+
+    _hideDebuggerSidebar: function()
+    {
+        if (this._toggleDebuggerSidebarButton.state === "hidden")
+            return;
+        this._toggleDebuggerSidebarButton.state = "hidden";
+        this._toggleDebuggerSidebarButton.title = WebInspector.UIString("Show debugger");
+        this.splitView.hideSidebarElement();
+        this.debugSidebarResizeWidgetElement.addStyleClass("hidden");
+        WebInspector.settings.debuggerSidebarHidden.set(true);
+    },
+
+    _fileRenamed: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode);
+        var name = /** @type {string} */ (event.data.name);
+        if (uiSourceCode.project().type() !== WebInspector.projectTypes.Snippets)
+            return;
+        WebInspector.scriptSnippetModel.renameScriptSnippet(uiSourceCode, name);
+        uiSourceCode.rename(name);
+    },
+        
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _snippetCreationRequested: function(event)
+    {
+        var uiSourceCode = WebInspector.scriptSnippetModel.createScriptSnippet();
+        this._showSourceLine(uiSourceCode);
+        
+        var shouldHideNavigator = !this._navigatorController.isNavigatorPinned();
+        if (this._navigatorController.isNavigatorHidden())
+            this._navigatorController.showNavigatorOverlay();
+        this._navigator.rename(uiSourceCode, callback.bind(this));
+    
+        /**
+         * @param {boolean} committed
+         */
+        function callback(committed)
+        {
+            if (shouldHideNavigator)
+                this._navigatorController.hideNavigatorOverlay();
+
+            if (!committed) {
+                WebInspector.scriptSnippetModel.deleteScriptSnippet(uiSourceCode);
+                return;
+            }
+
+            this._showSourceLine(uiSourceCode);
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _itemRenamingRequested: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        
+        var shouldHideNavigator = !this._navigatorController.isNavigatorPinned();
+        if (this._navigatorController.isNavigatorHidden())
+            this._navigatorController.showNavigatorOverlay();
+        this._navigator.rename(uiSourceCode, callback.bind(this));
+    
+        /**
+         * @param {boolean} committed
+         */
+        function callback(committed)
+        {
+            if (shouldHideNavigator && committed) {
+                this._navigatorController.hideNavigatorOverlay();
+                this._showSourceLine(uiSourceCode);
+            }
+        }
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _showLocalHistory: function(uiSourceCode)
+    {
+        WebInspector.RevisionHistoryView.showHistory(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    appendApplicableItems: function(event, contextMenu, target)
+    {
+        this._appendUISourceCodeItems(contextMenu, target);
+        this._appendFunctionItems(contextMenu, target);
+    },
+
+    /** 
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _mapFileSystemToNetwork: function(uiSourceCode)
+    {
+        WebInspector.SelectUISourceCodeForProjectTypeDialog.show(uiSourceCode.name(), WebInspector.projectTypes.Network, mapFileSystemToNetwork.bind(this), this.editorView.mainElement)                
+
+        /** 
+         * @param {WebInspector.UISourceCode} networkUISourceCode
+         */
+        function mapFileSystemToNetwork(networkUISourceCode)
+        {
+            this._workspace.addMapping(networkUISourceCode, uiSourceCode, WebInspector.fileSystemWorkspaceProvider);
+        }
+    },
+
+    /** 
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _removeNetworkMapping: function(uiSourceCode)
+    {
+        if (confirm(WebInspector.UIString("Are you sure you want to remove network mapping?")))
+            this._workspace.removeMapping(uiSourceCode);
+    },
+
+    /** 
+     * @param {WebInspector.UISourceCode} networkUISourceCode
+     */
+    _mapNetworkToFileSystem: function(networkUISourceCode)
+    {
+        WebInspector.SelectUISourceCodeForProjectTypeDialog.show(networkUISourceCode.name(), WebInspector.projectTypes.FileSystem, mapNetworkToFileSystem.bind(this), this.editorView.mainElement)                
+
+        /** 
+         * @param {WebInspector.UISourceCode} uiSourceCode
+         */
+        function mapNetworkToFileSystem(uiSourceCode)
+        {
+            this._workspace.addMapping(networkUISourceCode, uiSourceCode, WebInspector.fileSystemWorkspaceProvider);
+        }
+    },
+
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _appendUISourceCodeMappingItems: function(contextMenu, uiSourceCode)
+    {
+        if (uiSourceCode.project().type() === WebInspector.projectTypes.FileSystem) {
+            var hasMappings = !!uiSourceCode.url;
+            if (!hasMappings)
+                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Map to network resource\u2026" : "Map to Network Resource\u2026"), this._mapFileSystemToNetwork.bind(this, uiSourceCode));
+            else
+                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove network mapping" : "Remove Network Mapping"), this._removeNetworkMapping.bind(this, uiSourceCode));
+        }
+
+        if (uiSourceCode.project().type() === WebInspector.projectTypes.Network) {
+            /** 
+             * @param {WebInspector.Project} project
+             */
+            function filterProject(project)
+            {
+                return project.type() === WebInspector.projectTypes.FileSystem;
+            }
+
+            if (!this._workspace.projects().filter(filterProject).length)
+                return;
+            if (this._workspace.uiSourceCodeForURL(uiSourceCode.url) === uiSourceCode)
+                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Map to file system resource\u2026" : "Map to File System Resource\u2026"), this._mapNetworkToFileSystem.bind(this, uiSourceCode));
+        }
+    },
+
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    _appendUISourceCodeItems: function(contextMenu, target)
+    {
+        if (!(target instanceof WebInspector.UISourceCode))
+            return;
+
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (target);
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Local modifications\u2026" : "Local Modifications\u2026"), this._showLocalHistory.bind(this, uiSourceCode));
+
+        if (WebInspector.isolatedFileSystemManager.supportsFileSystems() && WebInspector.experimentsSettings.fileSystemProject.isEnabled())
+            this._appendUISourceCodeMappingItems(contextMenu, uiSourceCode);
+
+        var resource = WebInspector.resourceForURL(uiSourceCode.url);
+        if (resource && resource.request)
+            contextMenu.appendApplicableItems(resource.request);
+    },
+
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    _appendFunctionItems: function(contextMenu, target)
+    {
+        if (!(target instanceof WebInspector.RemoteObject))
+            return;
+        var remoteObject = /** @type {WebInspector.RemoteObject} */ (target);
+        if (remoteObject.type !== "function")
+            return;
+
+        function didGetDetails(error, response)
+        {
+            if (error) {
+                console.error(error);
+                return;
+            }
+            WebInspector.inspectorView.showPanelForAnchorNavigation(this);
+            var uiLocation = WebInspector.debuggerModel.rawLocationToUILocation(response.location);
+            this._showSourceLine(uiLocation.uiSourceCode, uiLocation.lineNumber);
+        }
+
+        function revealFunction()
+        {
+            DebuggerAgent.getFunctionDetails(remoteObject.objectId, didGetDetails.bind(this));
+        }
+
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Show function definition" : "Show Function Definition"), revealFunction.bind(this));
+    },
+
+    showGoToSourceDialog: function()
+    {
+        WebInspector.OpenResourceDialog.show(this, this.editorView.mainElement);
+    },
+
+    _dockSideChanged: function()
+    {
+        var dockSide = WebInspector.dockController.dockSide();
+        var vertically = dockSide === WebInspector.DockController.State.DockedToRight && WebInspector.settings.splitVerticallyWhenDockedToRight.get();
+        this._splitVertically(vertically);
+    },
+
+    /**
+     * @param {boolean} vertically
+     */
+    _splitVertically: function(vertically)
+    {
+        if (this.sidebarPaneView && vertically === !this.splitView.isVertical())
+            return;
+
+        if (this.sidebarPaneView)
+            this.sidebarPaneView.detach();
+
+        this.splitView.setVertical(!vertically);
+
+        if (!vertically) {
+            this.sidebarPaneView = new WebInspector.SidebarPaneStack();
+            for (var pane in this.sidebarPanes)
+                this.sidebarPaneView.addPane(this.sidebarPanes[pane]);
+
+            this.sidebarElement.appendChild(this.debugToolbar);
+        } else {
+            this._showDebuggerSidebar();
+
+            this.sidebarPaneView = new WebInspector.SplitView(true, this.name + "PanelSplitSidebarRatio", 0.5);
+
+            var group1 = new WebInspector.SidebarPaneStack();
+            group1.show(this.sidebarPaneView.firstElement());
+            group1.element.id = "scripts-sidebar-stack-pane";
+            group1.addPane(this.sidebarPanes.callstack);
+            group1.addPane(this.sidebarPanes.jsBreakpoints);
+            group1.addPane(this.sidebarPanes.domBreakpoints);
+            group1.addPane(this.sidebarPanes.xhrBreakpoints);
+            group1.addPane(this.sidebarPanes.eventListenerBreakpoints);
+            group1.addPane(this.sidebarPanes.workerList);
+
+            var group2 = new WebInspector.SidebarTabbedPane();
+            group2.show(this.sidebarPaneView.secondElement());
+            group2.addPane(this.sidebarPanes.scopechain);
+            group2.addPane(this.sidebarPanes.watchExpressions);
+
+            this.sidebarPaneView.firstElement().appendChild(this.debugToolbar);
+        }
+
+        this.sidebarPaneView.element.id = "scripts-debug-sidebar-contents";
+        this.sidebarPaneView.show(this.splitView.sidebarElement);
+
+        this.sidebarPanes.scopechain.expand();
+        this.sidebarPanes.jsBreakpoints.expand();
+        this.sidebarPanes.callstack.expand();
+
+        if (WebInspector.settings.watchExpressions.get().length > 0)
+            this.sidebarPanes.watchExpressions.expand();
+    },
+
+    __proto__: WebInspector.Panel.prototype
+}
diff --git a/Source/devtools/front_end/ScriptsPanelDescriptor.js b/Source/devtools/front_end/ScriptsPanelDescriptor.js
new file mode 100644
index 0000000..e64e8a5
--- /dev/null
+++ b/Source/devtools/front_end/ScriptsPanelDescriptor.js
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.PanelDescriptor}
+ * @implements {WebInspector.ContextMenu.Provider}
+ */
+WebInspector.ScriptsPanelDescriptor = function()
+{
+    WebInspector.PanelDescriptor.call(this, "scripts", WebInspector.UIString("Sources"), "ScriptsPanel", "ScriptsPanel.js");
+    WebInspector.ContextMenu.registerProvider(this);
+}
+
+WebInspector.ScriptsPanelDescriptor.prototype = {
+    /** 
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {Object} target
+     */
+    appendApplicableItems: function(event, contextMenu, target)
+    {
+        var hasApplicableItems = target instanceof WebInspector.UISourceCode;
+
+        if (!hasApplicableItems && target instanceof WebInspector.RemoteObject) {
+            var remoteObject = /** @type {WebInspector.RemoteObject} */ (target);
+            if (remoteObject.type !== "function")
+                return;
+        }
+
+        this.panel().appendApplicableItems(event, contextMenu, target);
+    },
+
+    registerShortcuts: function()
+    {
+        var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Sources Panel"));
+
+        section.addAlternateKeys(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.PauseContinue, WebInspector.UIString("Pause/Continue"));
+        section.addAlternateKeys(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.StepOver, WebInspector.UIString("Step over"));
+        section.addAlternateKeys(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.StepInto, WebInspector.UIString("Step into"));
+        section.addAlternateKeys(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.StepOut, WebInspector.UIString("Step out"));
+
+        var nextAndPrevFrameKeys = WebInspector.ScriptsPanelDescriptor.ShortcutKeys.NextCallFrame.concat(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.PrevCallFrame);
+        section.addRelatedKeys(nextAndPrevFrameKeys, WebInspector.UIString("Next/previous call frame"));
+
+        section.addAlternateKeys(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.EvaluateSelectionInConsole, WebInspector.UIString("Evaluate selection in console"));
+        section.addAlternateKeys(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.GoToMember, WebInspector.UIString("Go to member"));
+        section.addAlternateKeys(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.ToggleBreakpoint, WebInspector.UIString("Toggle breakpoint"));
+    },
+
+    __proto__: WebInspector.PanelDescriptor.prototype
+}
+
+WebInspector.ScriptsPanelDescriptor.ShortcutKeys = {
+    RunSnippet: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Enter, WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ],
+
+    PauseContinue: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F8),
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Slash, WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ],
+
+    StepOver: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F10),
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.SingleQuote, WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ],
+
+    StepInto: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11),
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ],
+
+    StepOut: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11, WebInspector.KeyboardShortcut.Modifiers.Shift),
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, WebInspector.KeyboardShortcut.Modifiers.Shift | WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ],
+
+    EvaluateSelectionInConsole: [
+        WebInspector.KeyboardShortcut.makeDescriptor("e", WebInspector.KeyboardShortcut.Modifiers.Shift | WebInspector.KeyboardShortcut.Modifiers.Ctrl)
+    ],
+
+    GoToMember: [
+        WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta | WebInspector.KeyboardShortcut.Modifiers.Shift)
+    ],
+
+    ToggleBreakpoint: [
+        WebInspector.KeyboardShortcut.makeDescriptor("b", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ],
+
+    NextCallFrame: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Period, WebInspector.KeyboardShortcut.Modifiers.Ctrl)
+    ],
+
+    PrevCallFrame: [
+        WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Comma, WebInspector.KeyboardShortcut.Modifiers.Ctrl)
+    ]
+};
diff --git a/Source/devtools/front_end/ScriptsSearchScope.js b/Source/devtools/front_end/ScriptsSearchScope.js
new file mode 100644
index 0000000..e8e357e
--- /dev/null
+++ b/Source/devtools/front_end/ScriptsSearchScope.js
@@ -0,0 +1,136 @@
+/*
+ * 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.SearchScope}
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.ScriptsSearchScope = function(workspace)
+{
+    // FIXME: Add title once it is used by search controller.
+    WebInspector.SearchScope.call(this)
+    this._searchId = 0;
+    this._workspace = workspace;
+}
+
+WebInspector.ScriptsSearchScope.prototype = {
+    /**
+     * @param {WebInspector.SearchConfig} searchConfig
+     * @param {function(WebInspector.FileBasedSearchResultsPane.SearchResult)} searchResultCallback
+     * @param {function(boolean)} searchFinishedCallback
+     */
+    performSearch: function(searchConfig, searchResultCallback, searchFinishedCallback)
+    {
+        this.stopSearch();
+        
+        var uiSourceCodes = this._sortedUISourceCodes();
+        var uiSourceCodeIndex = 0;
+        
+        function filterOutContentScripts(uiSourceCode)
+        {
+            return !uiSourceCode.isContentScript;
+        }
+        
+        if (!WebInspector.settings.searchInContentScripts.get())
+            uiSourceCodes = uiSourceCodes.filter(filterOutContentScripts);
+
+        function continueSearch()
+        {
+            // FIXME: Enable support for counting matches for incremental search.
+            // FIXME: Enable support for bounding search results/matches number to keep inspector responsive.
+            if (uiSourceCodeIndex < uiSourceCodes.length) {
+                var uiSourceCode = uiSourceCodes[uiSourceCodeIndex++];
+                uiSourceCode.searchInContent(searchConfig.query, !searchConfig.ignoreCase, searchConfig.isRegex, searchCallbackWrapper.bind(this, this._searchId, uiSourceCode));
+            } else 
+                searchFinishedCallback(true);
+        }
+
+        function searchCallbackWrapper(searchId, uiSourceCode, searchMatches)
+        {
+            if (searchId !== this._searchId) {
+                searchFinishedCallback(false);
+                return;
+            }
+            var searchResult = new WebInspector.FileBasedSearchResultsPane.SearchResult(uiSourceCode, searchMatches);
+            searchResultCallback(searchResult);
+            if (searchId !== this._searchId) {
+                searchFinishedCallback(false);
+                return;
+            }
+            continueSearch.call(this);
+        }
+        
+        continueSearch.call(this);
+        return uiSourceCodes.length;
+    },
+
+    stopSearch: function()
+    {
+        ++this._searchId;
+    },
+
+    /**
+     * @param {WebInspector.SearchConfig} searchConfig
+     */
+    createSearchResultsPane: function(searchConfig)
+    {
+        return new WebInspector.FileBasedSearchResultsPane(searchConfig);
+    },
+
+    /**
+     * @return {Array.<WebInspector.UISourceCode>}
+     */
+    _sortedUISourceCodes: function()
+    {
+        function filterOutAnonymous(uiSourceCode)
+        {
+            return !!uiSourceCode.originURL();
+        }
+        
+        function comparator(a, b)
+        {
+            return a.originURL().compareTo(b.originURL());   
+        }
+        
+        var projects = this._workspace.projects();
+        var uiSourceCodes = [];
+        for (var i = 0; i < projects.length; ++i) {
+            if (projects[i].isServiceProject())
+                continue;
+            uiSourceCodes = uiSourceCodes.concat(projects[i].uiSourceCodes());
+        }
+        
+        uiSourceCodes = uiSourceCodes.filter(filterOutAnonymous);
+        uiSourceCodes.sort(comparator);
+        
+        return uiSourceCodes;
+    },
+
+    __proto__: WebInspector.SearchScope.prototype
+}
diff --git a/Source/devtools/front_end/SearchController.js b/Source/devtools/front_end/SearchController.js
new file mode 100644
index 0000000..791b98a
--- /dev/null
+++ b/Source/devtools/front_end/SearchController.js
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
+ * Copyright (C) 2009 Joseph Pecoraro
+ * 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.SearchController = function()
+{
+    this._element = document.createElement("table");
+    this._element.className = "toolbar-search";
+    this._element.cellSpacing = 0;
+
+    this._firstRowElement = this._element.createChild("tr");
+    this._secondRowElement = this._element.createChild("tr", "hidden");
+
+    // Column 1
+    var searchControlElementColumn = this._firstRowElement.createChild("td"); 
+    this._searchControlElement = searchControlElementColumn.createChild("span", "toolbar-search-control");
+    this._searchInputElement = this._searchControlElement.createChild("input", "search-replace");
+    this._searchInputElement.id = "search-input-field";
+    this._searchInputElement.placeholder = WebInspector.UIString("Find");
+
+    this._filterControlElement = searchControlElementColumn.createChild("span", "toolbar-search-control");
+    this._filterControlElement.addStyleClass("hidden");
+    this._filterInputElement = this._filterControlElement.createChild("input", "filter");
+    this._filterInputElement.id = "filter-input-field";
+    this._filterInputElement.placeholder = WebInspector.UIString("Filter");
+
+    this._matchesElement = this._searchControlElement.createChild("label", "search-results-matches");
+    this._matchesElement.setAttribute("for", "search-input-field");
+
+    var searchNavigationElement = this._searchControlElement.createChild("div", "toolbar-search-navigation-controls");
+
+    this._searchNavigationPrevElement = searchNavigationElement.createChild("div", "toolbar-search-navigation toolbar-search-navigation-prev");
+    this._searchNavigationPrevElement.addEventListener("click", this._onPrevButtonSearch.bind(this), false);
+    this._searchNavigationPrevElement.title = WebInspector.UIString("Search Previous");
+
+    this._searchNavigationNextElement = searchNavigationElement.createChild("div", "toolbar-search-navigation toolbar-search-navigation-next"); 
+    this._searchNavigationNextElement.addEventListener("click", this._onNextButtonSearch.bind(this), false);
+    this._searchNavigationNextElement.title = WebInspector.UIString("Search Next");
+
+    this._searchInputElement.addEventListener("mousedown", this._onSearchFieldManualFocus.bind(this), false); // when the search field is manually selected
+    this._searchInputElement.addEventListener("keydown", this._onKeyDown.bind(this), true);
+    this._filterInputElement.addEventListener("keydown", this._onKeyDown.bind(this), true);
+    this._filterInputElement.addEventListener("input", this._onFilterInput.bind(this), false);
+    this._searchInputElement.addEventListener("input", this._onSearchInput.bind(this), false);
+
+    this._replaceInputElement = this._secondRowElement.createChild("td").createChild("input", "search-replace toolbar-replace-control");
+    this._replaceInputElement.addEventListener("keydown", this._onKeyDown.bind(this), true);
+    this._replaceInputElement.placeholder = WebInspector.UIString("Replace");
+
+    // Column 2
+    this._findButtonElement = this._firstRowElement.createChild("td").createChild("button", "hidden");
+    this._findButtonElement.textContent = WebInspector.UIString("Find");
+    this._findButtonElement.tabIndex = -1;
+    this._findButtonElement.addEventListener("click", this._onNextButtonSearch.bind(this), false);
+
+    this._replaceButtonElement = this._secondRowElement.createChild("td").createChild("button");
+    this._replaceButtonElement.textContent = WebInspector.UIString("Replace");
+    this._replaceButtonElement.disabled = true;
+    this._replaceButtonElement.tabIndex = -1;
+    this._replaceButtonElement.addEventListener("click", this._replace.bind(this), false);
+
+    // Column 3
+    this._prevButtonElement = this._firstRowElement.createChild("td").createChild("button", "hidden");
+    this._prevButtonElement.textContent = WebInspector.UIString("Previous");
+    this._prevButtonElement.disabled = true;
+    this._prevButtonElement.tabIndex = -1;
+    this._prevButtonElement.addEventListener("click", this._onPrevButtonSearch.bind(this), false);
+
+    this._replaceAllButtonElement = this._secondRowElement.createChild("td").createChild("button");
+    this._replaceAllButtonElement.textContent = WebInspector.UIString("Replace All");
+    this._replaceAllButtonElement.addEventListener("click", this._replaceAll.bind(this), false);
+
+    // Column 4
+    this._replaceElement = this._firstRowElement.createChild("td").createChild("span");
+
+    this._replaceCheckboxElement = this._replaceElement.createChild("input");
+    this._replaceCheckboxElement.type = "checkbox";
+    this._replaceCheckboxElement.id = "search-replace-trigger";
+    this._replaceCheckboxElement.addEventListener("click", this._updateSecondRowVisibility.bind(this), false);
+
+    this._replaceLabelElement = this._replaceElement.createChild("label");
+    this._replaceLabelElement.textContent = WebInspector.UIString("Replace");
+    this._replaceLabelElement.setAttribute("for", "search-replace-trigger");
+
+    // Column 5
+    this._filterCheckboxContainer = this._firstRowElement.createChild("td").createChild("span");
+
+    this._filterCheckboxElement = this._filterCheckboxContainer.createChild("input");
+    this._filterCheckboxElement.type = "checkbox";
+    this._filterCheckboxElement.id = "filter-trigger";
+    this._filterCheckboxElement.addEventListener("click", this._filterCheckboxClick.bind(this), false);
+  
+    this._filterLabelElement = this._filterCheckboxContainer.createChild("label");
+    this._filterLabelElement.textContent = WebInspector.UIString("Filter");
+    this._filterLabelElement.setAttribute("for", "filter-trigger");
+
+    // Column 6
+    var cancelButtonElement = this._firstRowElement.createChild("td").createChild("button");
+    cancelButtonElement.textContent = WebInspector.UIString("Cancel");
+    cancelButtonElement.tabIndex = -1;
+    cancelButtonElement.addEventListener("click", this.cancelSearch.bind(this), false);
+}
+
+WebInspector.SearchController.prototype = {
+    updateSearchMatchesCount: function(matches, panel)
+    {
+        if (!panel)
+            panel = WebInspector.inspectorView.currentPanel();
+
+        panel.currentSearchMatches = matches;
+
+        if (panel === WebInspector.inspectorView.currentPanel())
+            this._updateSearchMatchesCountAndCurrentMatchIndex(WebInspector.inspectorView.currentPanel().currentQuery ? matches : 0, -1);
+    },
+
+    updateCurrentMatchIndex: function(currentMatchIndex, panel)
+    {
+        if (panel === WebInspector.inspectorView.currentPanel())
+            this._updateSearchMatchesCountAndCurrentMatchIndex(panel.currentSearchMatches, currentMatchIndex);
+    },
+
+    cancelSearch: function()
+    {
+        if (!this._searchIsVisible)
+            return;
+        if (this._filterCheckboxElement.checked) {
+            this._filterCheckboxElement.checked = false;
+            this._switchFilterToSearch();
+        } 
+        delete this._searchIsVisible;
+        WebInspector.inspectorView.setFooterElement(null);
+        this.resetSearch();
+    },
+
+    resetSearch: function()
+    {
+        this._performSearch("", false, false);
+        this._updateReplaceVisibility();
+        this._matchesElement.textContent = "";
+    },
+
+    disableSearchUntilExplicitAction: function()
+    {
+        this._performSearch("", false, false);
+    },
+
+    /**
+     * @param {Event} event
+     * @return {boolean}
+     */
+    handleShortcut: function(event)
+    {
+        var isMac = WebInspector.isMac();
+
+        switch (event.keyIdentifier) {
+            case "U+0046": // F key
+                if (isMac)
+                    var isFindKey = event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey;
+                else
+                    var isFindKey = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey;
+
+                if (isFindKey) {
+                    this.showSearchField();
+                    event.consume(true);
+                    return true;
+                }
+                break;
+
+            case "F3":
+                if (!isMac) {
+                    this.showSearchField();
+                    event.consume();
+                }
+                break;
+
+            case "U+0047": // G key
+                var currentPanel = WebInspector.inspectorView.currentPanel();
+
+                if (isMac && event.metaKey && !event.ctrlKey && !event.altKey) {
+                    if (event.shiftKey)
+                        currentPanel.jumpToPreviousSearchResult();
+                    else
+                        currentPanel.jumpToNextSearchResult();
+                    event.consume(true);
+                    return true;
+                }
+                break;
+        }
+        return false;
+    },
+
+    _updateSearchNavigationButtonState: function(enabled)
+    {
+        this._replaceButtonElement.disabled = !enabled;
+        this._prevButtonElement.disabled = !enabled;
+        var panel = WebInspector.inspectorView.currentPanel();
+        if (enabled) {
+            this._searchNavigationPrevElement.addStyleClass("enabled");
+            this._searchNavigationNextElement.addStyleClass("enabled");
+        } else {
+            this._searchNavigationPrevElement.removeStyleClass("enabled");
+            this._searchNavigationNextElement.removeStyleClass("enabled");
+        }
+    },
+
+    /**
+     * @param {number} matches
+     * @param {number} currentMatchIndex
+     */
+    _updateSearchMatchesCountAndCurrentMatchIndex: function(matches, currentMatchIndex)
+    {
+        if (matches === 0 || currentMatchIndex >= 0)
+            this._matchesElement.textContent = WebInspector.UIString("%d of %d", currentMatchIndex + 1, matches);
+        this._updateSearchNavigationButtonState(matches > 0);
+    },
+
+    showSearchField: function()
+    {
+        WebInspector.inspectorView.setFooterElement(this._element);
+        this._updateReplaceVisibility();
+        this._updateFilterVisibility();
+        if (WebInspector.currentFocusElement() !== this._searchInputElement) {
+            var selection = window.getSelection();
+            if (selection.rangeCount)
+                this._searchInputElement.value = selection.toString().replace(/\r?\n.*/, "");
+        }
+        this._performSearch(this._searchInputElement.value, true, false);
+        this._searchInputElement.focus();
+        this._searchInputElement.select();
+        this._searchIsVisible = true;
+    },
+
+    _switchFilterToSearch: function()
+    {
+        this._filterControlElement.addStyleClass("hidden");
+        this._searchControlElement.removeStyleClass("hidden");
+        this._searchInputElement.focus();
+        this._searchInputElement.select();
+        this._searchInputElement.value = this._filterInputElement.value;
+        this.resetFilter();
+    },
+
+    _switchSearchToFilter: function()
+    {
+        this._filterControlElement.removeStyleClass("hidden");
+        this._searchControlElement.addStyleClass("hidden");
+        this._filterInputElement.focus();
+        this._filterInputElement.select();
+        this._filterInputElement.value = this._searchInputElement.value;
+        this.resetSearch();
+    },
+    
+    _updateFilterVisibility: function()
+    {
+        if (WebInspector.inspectorView.currentPanel().canFilter())
+            this._filterCheckboxContainer.removeStyleClass("hidden");
+        else
+            this._filterCheckboxContainer.addStyleClass("hidden");
+    },
+  
+    _updateReplaceVisibility: function()
+    {
+        var panel = WebInspector.inspectorView.currentPanel();
+        if (panel && panel.canSearchAndReplace())
+            this._replaceElement.removeStyleClass("hidden");
+        else {
+            this._replaceElement.addStyleClass("hidden");
+            this._replaceCheckboxElement.checked = false;
+            this._updateSecondRowVisibility();
+        }
+    },
+
+    _onSearchFieldManualFocus: function(event)
+    {
+        WebInspector.setCurrentFocusElement(event.target);
+    },
+
+    _onKeyDown: function(event)
+    {
+        // Escape Key will clear the field and clear the search results
+        if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
+            event.consume(true);
+            this.cancelSearch();
+            WebInspector.setCurrentFocusElement(WebInspector.previousFocusElement());
+            if (WebInspector.currentFocusElement() === event.target)
+                WebInspector.currentFocusElement().select();
+            return false;
+        }
+
+        if (isEnterKey(event)) {
+            if (event.target === this._searchInputElement)
+                this._performSearch(event.target.value, true, event.shiftKey);
+            else if (event.target === this._replaceInputElement)
+                this._replace();
+        }
+    },
+
+    _onNextButtonSearch: function(event)
+    {
+        // Simulate next search on search-navigation-button click.
+        this._performSearch(this._searchInputElement.value, true, false);
+        this._searchInputElement.focus();
+    },
+
+    _onPrevButtonSearch: function(event)
+    {
+        if (!this._searchNavigationPrevElement.hasStyleClass("enabled"))
+            return;
+        // Simulate previous search on search-navigation-button click.
+        this._performSearch(this._searchInputElement.value, true, true);
+        this._searchInputElement.focus();
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} forceSearch
+     * @param {boolean} isBackwardSearch
+     */
+    _performSearch: function(query, forceSearch, isBackwardSearch)
+    {
+        if (!query || !query.length) {
+            delete this._currentQuery;
+
+            for (var panelName in WebInspector.panels) {
+                var panel = WebInspector.panels[panelName];
+                var hadCurrentQuery = !!panel.currentQuery;
+                delete panel.currentQuery;
+                if (hadCurrentQuery)
+                    panel.searchCanceled();
+            }
+            this._updateSearchMatchesCountAndCurrentMatchIndex(0, -1);
+            return;
+        }
+
+        var currentPanel = WebInspector.inspectorView.currentPanel();
+        if (query === currentPanel.currentQuery && currentPanel.currentQuery === this._currentQuery) {
+            // When this is the same query and a forced search, jump to the next
+            // search result for a good user experience.
+            if (forceSearch) {
+                if (!isBackwardSearch)
+                    currentPanel.jumpToNextSearchResult();
+                else if (isBackwardSearch)
+                    currentPanel.jumpToPreviousSearchResult();
+            }
+            return;
+        }
+
+        if (!forceSearch && query.length < 3 && !this._currentQuery)
+            return;
+
+        this._currentQuery = query;
+
+        currentPanel.currentQuery = query;
+        currentPanel.performSearch(query);
+    },
+
+    _updateSecondRowVisibility: function()
+    {
+        if (!this._searchIsVisible)
+            return;
+        if (this._replaceCheckboxElement.checked) {
+            this._element.addStyleClass("toolbar-search-replace");
+            this._secondRowElement.removeStyleClass("hidden");
+            this._prevButtonElement.removeStyleClass("hidden");
+            this._findButtonElement.removeStyleClass("hidden");
+            this._replaceCheckboxElement.tabIndex = -1;
+            this._replaceInputElement.focus();
+        } else {
+            this._element.removeStyleClass("toolbar-search-replace");
+            this._secondRowElement.addStyleClass("hidden");
+            this._prevButtonElement.addStyleClass("hidden");
+            this._findButtonElement.addStyleClass("hidden");
+            this._replaceCheckboxElement.tabIndex = 0;
+            this._searchInputElement.focus();
+        }
+        WebInspector.inspectorView.setFooterElement(this._element);
+    },
+
+    _replace: function()
+    {
+        var currentPanel = WebInspector.inspectorView.currentPanel();
+        currentPanel.replaceSelectionWith(this._replaceInputElement.value);
+        var query = this._currentQuery;
+        delete this._currentQuery;
+        this._performSearch(query, true, false);
+    },
+
+    _replaceAll: function()
+    {
+        var currentPanel = WebInspector.inspectorView.currentPanel();
+        currentPanel.replaceAllWith(this._searchInputElement.value, this._replaceInputElement.value);
+    },
+  
+    _filterCheckboxClick: function()
+    {
+        if (this._filterCheckboxElement.checked) { 
+            this._switchSearchToFilter();
+            this._performFilter(this._filterInputElement.value);
+        } else {
+            this._switchFilterToSearch();
+            this._performSearch(this._searchInputElement.value, false, false);
+        }
+    },
+    
+    /**
+     * @param {string} query
+     */
+    _performFilter: function(query)
+    {
+        WebInspector.inspectorView.currentPanel().performFilter(query);
+    },
+  
+    _onFilterInput: function(event)
+    {
+        this._performFilter(event.target.value);
+    },
+  
+    _onSearchInput: function(event)
+    {
+        this._performSearch(event.target.value, false, false);
+    },
+    
+    resetFilter: function()
+    {
+        this._performFilter("");
+    }
+}
+
+/**
+ * @type {?WebInspector.SearchController}
+ */
+WebInspector.searchController = null;
diff --git a/Source/devtools/front_end/Section.js b/Source/devtools/front_end/Section.js
new file mode 100644
index 0000000..ef7f1ee
--- /dev/null
+++ b/Source/devtools/front_end/Section.js
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2007 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @param {string|Element} title
+ * @param {string=} subtitle
+ */
+WebInspector.Section = function(title, subtitle)
+{
+    this.element = document.createElement("div");
+    this.element.className = "section";
+    this.element._section = this;
+
+    this.headerElement = document.createElement("div");
+    this.headerElement.className = "header";
+
+    this.titleElement = document.createElement("div");
+    this.titleElement.className = "title";
+
+    this.subtitleElement = document.createElement("div");
+    this.subtitleElement.className = "subtitle";
+
+    this.headerElement.appendChild(this.subtitleElement);
+    this.headerElement.appendChild(this.titleElement);
+
+    this.headerElement.addEventListener("click", this.handleClick.bind(this), false);
+    this.element.appendChild(this.headerElement);
+
+    this.title = title;
+    this.subtitle = subtitle;
+    this._expanded = false;
+}
+
+WebInspector.Section.prototype = {
+    get title()
+    {
+        return this._title;
+    },
+
+    set title(x)
+    {
+        if (this._title === x)
+            return;
+        this._title = x;
+
+        if (x instanceof Node) {
+            this.titleElement.removeChildren();
+            this.titleElement.appendChild(x);
+        } else
+          this.titleElement.textContent = x;
+    },
+
+    get subtitle()
+    {
+        return this._subtitle;
+    },
+
+    set subtitle(x)
+    {
+        if (this._subtitle === x)
+            return;
+        this._subtitle = x;
+        this.subtitleElement.textContent = x;
+    },
+
+    get subtitleAsTextForTest()
+    {
+        var result = this.subtitleElement.textContent;
+        var child = this.subtitleElement.querySelector("[data-uncopyable]");
+        if (child) {
+            var linkData = child.getAttribute("data-uncopyable");
+            if (linkData)
+                result += linkData;
+        }
+        return result;
+    },
+
+    get expanded()
+    {
+        return this._expanded;
+    },
+
+    set expanded(x)
+    {
+        if (x)
+            this.expand();
+        else
+            this.collapse();
+    },
+
+    get populated()
+    {
+        return this._populated;
+    },
+
+    set populated(x)
+    {
+        this._populated = x;
+        if (!x && this._expanded) {
+            this.onpopulate();
+            this._populated = true;
+        }
+    },
+
+    onpopulate: function()
+    {
+        // Overriden by subclasses.
+    },
+
+    get firstSibling()
+    {
+        var parent = this.element.parentElement;
+        if (!parent)
+            return null;
+
+        var childElement = parent.firstChild;
+        while (childElement) {
+            if (childElement._section)
+                return childElement._section;
+            childElement = childElement.nextSibling;
+        }
+
+        return null;
+    },
+
+    get lastSibling()
+    {
+        var parent = this.element.parentElement;
+        if (!parent)
+            return null;
+
+        var childElement = parent.lastChild;
+        while (childElement) {
+            if (childElement._section)
+                return childElement._section;
+            childElement = childElement.previousSibling;
+        }
+
+        return null;
+    },
+
+    get nextSibling()
+    {
+        var curElement = this.element;
+        do {
+            curElement = curElement.nextSibling;
+        } while (curElement && !curElement._section);
+
+        return curElement ? curElement._section : null;
+    },
+
+    get previousSibling()
+    {
+        var curElement = this.element;
+        do {
+            curElement = curElement.previousSibling;
+        } while (curElement && !curElement._section);
+
+        return curElement ? curElement._section : null;
+    },
+
+    expand: function()
+    {
+        if (this._expanded)
+            return;
+        this._expanded = true;
+        this.element.addStyleClass("expanded");
+
+        if (!this._populated) {
+            this.onpopulate();
+            this._populated = true;
+        }
+    },
+
+    collapse: function()
+    {
+        if (!this._expanded)
+            return;
+        this._expanded = false;
+        this.element.removeStyleClass("expanded");
+    },
+
+    toggleExpanded: function()
+    {
+        this.expanded = !this.expanded;
+    },
+
+    handleClick: function(event)
+    {
+        this.toggleExpanded();
+        event.consume();
+    }
+}
diff --git a/Source/devtools/front_end/Settings.js b/Source/devtools/front_end/Settings.js
new file mode 100644
index 0000000..afce623
--- /dev/null
+++ b/Source/devtools/front_end/Settings.js
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+
+var Preferences = {
+    maxInlineTextChildLength: 80,
+    minConsoleHeight: 75,
+    minSidebarWidth: 100,
+    minSidebarHeight: 75,
+    minElementsSidebarWidth: 200,
+    minElementsSidebarHeight: 200,
+    minScriptsSidebarWidth: 200,
+    applicationTitle: "Developer Tools - %s",
+    experimentsEnabled: false
+}
+
+var Capabilities = {
+    canShowFPSCounter: false,
+    canContinuouslyPaint: false,
+    canInspectWorkers: false
+}
+
+/**
+ * @constructor
+ */
+WebInspector.Settings = function()
+{
+    this._eventSupport = new WebInspector.Object();
+    this._registry = /** @type {!Object.<string, !WebInspector.Setting>} */ ({});
+
+    this.colorFormat = this.createSetting("colorFormat", "original");
+    this.consoleHistory = this.createSetting("consoleHistory", []);
+    this.domWordWrap = this.createSetting("domWordWrap", true);
+    this.eventListenersFilter = this.createSetting("eventListenersFilter", "all");
+    this.lastActivePanel = this.createSetting("lastActivePanel", "elements");
+    this.lastViewedScriptFile = this.createSetting("lastViewedScriptFile", "application");
+    this.monitoringXHREnabled = this.createSetting("monitoringXHREnabled", false);
+    this.preserveConsoleLog = this.createSetting("preserveConsoleLog", false);
+    this.resourcesLargeRows = this.createSetting("resourcesLargeRows", true);
+    this.resourcesSortOptions = this.createSetting("resourcesSortOptions", {timeOption: "responseTime", sizeOption: "transferSize"});
+    this.resourceViewTab = this.createSetting("resourceViewTab", "preview");
+    this.showInheritedComputedStyleProperties = this.createSetting("showInheritedComputedStyleProperties", false);
+    this.showUserAgentStyles = this.createSetting("showUserAgentStyles", true);
+    this.watchExpressions = this.createSetting("watchExpressions", []);
+    this.breakpoints = this.createSetting("breakpoints", []);
+    this.eventListenerBreakpoints = this.createSetting("eventListenerBreakpoints", []);
+    this.domBreakpoints = this.createSetting("domBreakpoints", []);
+    this.xhrBreakpoints = this.createSetting("xhrBreakpoints", []);
+    this.sourceMapsEnabled = this.createSetting("sourceMapsEnabled", true);
+    this.cacheDisabled = this.createSetting("cacheDisabled", false);
+    this.overrideUserAgent = this.createSetting("overrideUserAgent", "");
+    this.userAgent = this.createSetting("userAgent", "");
+    this.deviceMetrics = this.createSetting("deviceMetrics", "");
+    this.deviceFitWindow = this.createSetting("deviceFitWindow", false);
+    this.emulateTouchEvents = this.createSetting("emulateTouchEvents", false);
+    this.showPaintRects = this.createSetting("showPaintRects", false);
+    this.continuousPainting = this.createSetting("continuousPainting", false);
+    this.showDebugBorders = this.createSetting("showDebugBorders", false);
+    this.showFPSCounter = this.createSetting("showFPSCounter", false);
+    this.showShadowDOM = this.createSetting("showShadowDOM", false);
+    this.zoomLevel = this.createSetting("zoomLevel", 0);
+    this.savedURLs = this.createSetting("savedURLs", {});
+    this.javaScriptDisabled = this.createSetting("javaScriptDisabled", false);
+    this.geolocationOverride = this.createSetting("geolocationOverride", "");
+    this.deviceOrientationOverride = this.createSetting("deviceOrientationOverride", "");
+    this.showHeapSnapshotObjectsHiddenProperties = this.createSetting("showHeaSnapshotObjectsHiddenProperties", false);
+    this.showNativeSnapshotUninstrumentedSize = this.createSetting("showNativeSnapshotUninstrumentedSize", false);
+    this.searchInContentScripts = this.createSetting("searchInContentScripts", false);
+    this.textEditorIndent = this.createSetting("textEditorIndent", "    ");
+    this.lastDockState = this.createSetting("lastDockState", "");
+    this.cssReloadEnabled = this.createSetting("cssReloadEnabled", false);
+    this.cssReloadTimeout = this.createSetting("cssReloadTimeout", 1000);
+    this.showCpuOnTimelineRuler = this.createSetting("showCpuOnTimelineRuler", false);
+    this.timelineStackFramesToCapture = this.createSetting("timelineStackFramesToCapture", 30);
+    this.timelineLimitStackFramesFlag = this.createSetting("timelineLimitStackFramesFlag", false);
+    this.showMetricsRulers = this.createSetting("showMetricsRulers", false);
+    this.emulatedCSSMedia = this.createSetting("emulatedCSSMedia", "print");
+    this.showToolbarIcons = this.createSetting("showToolbarIcons", false);
+    this.workerInspectorWidth = this.createSetting("workerInspectorWidth", 600);
+    this.workerInspectorHeight = this.createSetting("workerInspectorHeight", 600);
+    this.messageURLFilters = this.createSetting("messageURLFilters", {});
+    this.splitVerticallyWhenDockedToRight = this.createSetting("splitVerticallyWhenDockedToRight", true);
+    this.visiblePanels = this.createSetting("visiblePanels", {});
+    this.shortcutPanelSwitch = this.createSetting("shortcutPanelSwitch", false);
+    this.portForwardings = this.createSetting("portForwardings", []);
+    this.codemirror = this.createSetting("codemirror", false);
+}
+
+WebInspector.Settings.prototype = {
+    /**
+     * @param {string} key
+     * @param {*} defaultValue
+     * @return {!WebInspector.Setting}
+     */
+    createSetting: function(key, defaultValue)
+    {
+        if (!this._registry[key])
+            this._registry[key] = new WebInspector.Setting(key, defaultValue, this._eventSupport, window.localStorage);
+        return this._registry[key];
+    }
+}
+
+/**
+ * @constructor
+ * @param {string} name
+ * @param {*} defaultValue
+ * @param {!WebInspector.Object} eventSupport
+ * @param {?Storage} storage
+ */
+WebInspector.Setting = function(name, defaultValue, eventSupport, storage)
+{
+    this._name = name;
+    this._defaultValue = defaultValue;
+    this._eventSupport = eventSupport;
+    this._storage = storage;
+}
+
+WebInspector.Setting.prototype = {
+    addChangeListener: function(listener, thisObject)
+    {
+        this._eventSupport.addEventListener(this._name, listener, thisObject);
+    },
+
+    removeChangeListener: function(listener, thisObject)
+    {
+        this._eventSupport.removeEventListener(this._name, listener, thisObject);
+    },
+
+    get name()
+    {
+        return this._name;
+    },
+
+    get: function()
+    {
+        if (typeof this._value !== "undefined")
+            return this._value;
+
+        this._value = this._defaultValue;
+        if (this._storage && this._name in this._storage) {
+            try {
+                this._value = JSON.parse(this._storage[this._name]);
+            } catch(e) {
+                delete this._storage[this._name];
+            }
+        }
+        return this._value;
+    },
+
+    set: function(value)
+    {
+        this._value = value;
+        if (this._storage) {
+            try {
+                this._storage[this._name] = JSON.stringify(value);
+            } catch(e) {
+                console.error("Error saving setting with name:" + this._name);
+            }
+        }
+        this._eventSupport.dispatchEventToListeners(this._name, value);
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.ExperimentsSettings = function()
+{
+    this._setting = WebInspector.settings.createSetting("experiments", {});
+    this._experiments = [];
+    this._enabledForTest = {};
+
+    // Add currently running experiments here.
+    this.nativeMemorySnapshots = this._createExperiment("nativeMemorySnapshots", "Native memory profiling");
+    this.nativeMemoryTimeline = this._createExperiment("nativeMemoryTimeline", "Native memory timeline");
+    this.fileSystemInspection = this._createExperiment("fileSystemInspection", "FileSystem inspection");
+    this.canvasInspection = this._createExperiment("canvasInspection ", "Canvas inspection");
+    this.sass = this._createExperiment("sass", "Support for Sass");
+    this.cssRegions = this._createExperiment("cssRegions", "CSS Regions Support");
+    this.showOverridesInDrawer = this._createExperiment("showOverridesInDrawer", "Show Overrides in drawer");
+    this.fileSystemProject = this._createExperiment("fileSystemProject", "File system folders in Sources Panel");
+    this.showWhitespaceInEditor = this._createExperiment("showWhitespaceInEditor", "Show whitespace characters in editor");
+    this.customizableToolbar = this._createExperiment("customizableToolbar", "Enable toolbar customization");
+    this.tethering = this._createExperiment("tethering", "Enable reverse port forwarding");
+    this.drawerOverlay = this._createExperiment("drawerOverlay", "Open console as overlay");
+    this.heapObjectsTracking = this._createExperiment("heapObjectsTracking", "Enable heap objects tracking profile type");
+
+    this._cleanUpSetting();
+}
+
+WebInspector.ExperimentsSettings.prototype = {
+    /**
+     * @return {Array.<WebInspector.Experiment>}
+     */
+    get experiments()
+    {
+        return this._experiments.slice();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    get experimentsEnabled()
+    {
+        return Preferences.experimentsEnabled || ("experiments" in WebInspector.queryParamsObject);
+    },
+
+    /**
+     * @param {string} experimentName
+     * @param {string} experimentTitle
+     * @return {WebInspector.Experiment}
+     */
+    _createExperiment: function(experimentName, experimentTitle)
+    {
+        var experiment = new WebInspector.Experiment(this, experimentName, experimentTitle);
+        this._experiments.push(experiment);
+        return experiment;
+    },
+
+    /**
+     * @param {string} experimentName
+     * @return {boolean}
+     */
+    isEnabled: function(experimentName)
+    {
+        if (this._enabledForTest[experimentName])
+            return true;
+
+        if (!this.experimentsEnabled)
+            return false;
+        
+        var experimentsSetting = this._setting.get();
+        return experimentsSetting[experimentName];
+    },
+
+    /**
+     * @param {string} experimentName
+     * @param {boolean} enabled
+     */
+    setEnabled: function(experimentName, enabled)
+    {
+        var experimentsSetting = this._setting.get();
+        experimentsSetting[experimentName] = enabled;
+        this._setting.set(experimentsSetting);
+    },
+
+    /**
+     * @param {string} experimentName
+     */
+    _enableForTest: function(experimentName)
+    {
+        this._enabledForTest[experimentName] = true;
+    },
+
+    _cleanUpSetting: function()
+    {
+        var experimentsSetting = this._setting.get();
+        var cleanedUpExperimentSetting = {};
+        for (var i = 0; i < this._experiments.length; ++i) {
+            var experimentName = this._experiments[i].name;
+            if (experimentsSetting[experimentName])
+                cleanedUpExperimentSetting[experimentName] = true;
+        }
+        this._setting.set(cleanedUpExperimentSetting);
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.ExperimentsSettings} experimentsSettings
+ * @param {string} name
+ * @param {string} title
+ */
+WebInspector.Experiment = function(experimentsSettings, name, title)
+{
+    this._name = name;
+    this._title = title;
+    this._experimentsSettings = experimentsSettings;
+}
+
+WebInspector.Experiment.prototype = {
+    /**
+     * @return {string}
+     */
+    get name()
+    {
+        return this._name;
+    },
+
+    /**
+     * @return {string}
+     */
+    get title()
+    {
+        return this._title;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isEnabled: function()
+    {
+        return this._experimentsSettings.isEnabled(this._name);
+    },
+
+    /**
+     * @param {boolean} enabled
+     */
+    setEnabled: function(enabled)
+    {
+        return this._experimentsSettings.setEnabled(this._name, enabled);
+    },
+
+    enableForTest: function()
+    {
+        this._experimentsSettings._enableForTest(this._name);
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.VersionController = function()
+{
+}
+
+WebInspector.VersionController.currentVersion = 2;
+
+WebInspector.VersionController.prototype = {
+    updateVersion: function()
+    {
+        var versionSetting = WebInspector.settings.createSetting("inspectorVersion", 0);
+        var currentVersion = WebInspector.VersionController.currentVersion;
+        var oldVersion = versionSetting.get();
+        var methodsToRun = this._methodsToRunToUpdateVersion(oldVersion, currentVersion);
+        for (var i = 0; i < methodsToRun.length; ++i)
+            this[methodsToRun[i]].call(this);
+        versionSetting.set(currentVersion);
+    },
+
+    /**
+     * @param {number} oldVersion
+     * @param {number} currentVersion
+     */
+    _methodsToRunToUpdateVersion: function(oldVersion, currentVersion)
+    {
+        var result = [];
+        for (var i = oldVersion; i < currentVersion; ++i)
+            result.push("_updateVersionFrom" + i + "To" + (i + 1));
+        return result;
+    },
+
+    _updateVersionFrom0To1: function()
+    {
+        this._clearBreakpointsWhenTooMany(WebInspector.settings.breakpoints, 500000);
+    },
+
+    _updateVersionFrom1To2: function()
+    {
+        var versionSetting = WebInspector.settings.createSetting("previouslyViewedFiles", []);
+        versionSetting.set([]);
+    },
+
+    /**
+     * @param {WebInspector.Setting} breakpointsSetting
+     * @param {number} maxBreakpointsCount
+     */
+    _clearBreakpointsWhenTooMany: function(breakpointsSetting, maxBreakpointsCount)
+    {
+        // If there are too many breakpoints in a storage, it is likely due to a recent bug that caused
+        // periodical breakpoints duplication leading to inspector slowness.
+        if (breakpointsSetting.get().length > maxBreakpointsCount)
+            breakpointsSetting.set([]);
+    }
+}
+
+WebInspector.settings = new WebInspector.Settings();
+WebInspector.experimentsSettings = new WebInspector.ExperimentsSettings();
diff --git a/Source/devtools/front_end/SettingsScreen.js b/Source/devtools/front_end/SettingsScreen.js
new file mode 100644
index 0000000..77a285b
--- /dev/null
+++ b/Source/devtools/front_end/SettingsScreen.js
@@ -0,0 +1,983 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {!function()} onHide
+ * @extends {WebInspector.HelpScreen}
+ */
+WebInspector.SettingsScreen = function(onHide)
+{
+    WebInspector.HelpScreen.call(this);
+    this.element.id = "settings-screen";
+
+    /** @type {function()} */
+    this._onHide = onHide;
+
+    this._tabbedPane = new WebInspector.TabbedPane();
+    this._tabbedPane.element.addStyleClass("help-window-main");
+    var settingsLabelElement = document.createElement("div");
+    settingsLabelElement.className = "help-window-label";
+    settingsLabelElement.createTextChild(WebInspector.UIString("Settings"));
+    this._tabbedPane.element.insertBefore(settingsLabelElement, this._tabbedPane.element.firstChild);
+    this._tabbedPane.element.appendChild(this._createCloseButton());
+    this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.General, WebInspector.UIString("General"), new WebInspector.GenericSettingsTab());
+    if (!WebInspector.experimentsSettings.showOverridesInDrawer.isEnabled())
+        this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Overrides, WebInspector.UIString("Overrides"), new WebInspector.OverridesSettingsTab());
+    if (WebInspector.experimentsSettings.fileSystemProject.isEnabled())
+        this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Workspace, WebInspector.UIString("Workspace"), new WebInspector.WorkspaceSettingsTab());
+    if (WebInspector.experimentsSettings.tethering.isEnabled())
+        this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Tethering, WebInspector.UIString("Port forwarding"), new WebInspector.TetheringSettingsTab());
+    if (WebInspector.experimentsSettings.experimentsEnabled)
+        this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Experiments, WebInspector.UIString("Experiments"), new WebInspector.ExperimentsSettingsTab());
+    this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Shortcuts, WebInspector.UIString("Shortcuts"), WebInspector.shortcutsScreen.createShortcutsTabView());
+    this._tabbedPane.shrinkableTabs = false;
+    this._tabbedPane.verticalTabLayout = true;
+
+    this._lastSelectedTabSetting = WebInspector.settings.createSetting("lastSelectedSettingsTab", WebInspector.SettingsScreen.Tabs.General);
+    this.selectTab(this._lastSelectedTabSetting.get());
+    this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
+}
+
+WebInspector.SettingsScreen.Tabs = {
+    General: "general",
+    Overrides: "overrides",
+    Workspace: "workspace",
+    Tethering: "tethering",
+    Experiments: "experiments",
+    Shortcuts: "shortcuts"
+}
+
+WebInspector.SettingsScreen.prototype = {
+    /**
+     * @param {string} tabId
+     */
+    selectTab: function(tabId)
+    {
+        this._tabbedPane.selectTab(tabId);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _tabSelected: function(event)
+    {
+        this._lastSelectedTabSetting.set(this._tabbedPane.selectedTabId);
+    },
+
+    /**
+     * @override
+     */
+    wasShown: function()
+    {
+        this._tabbedPane.show(this.element);
+        WebInspector.HelpScreen.prototype.wasShown.call(this);
+    },
+
+    /**
+     * @override
+     */
+    isClosingKey: function(keyCode)
+    {
+        return [
+            WebInspector.KeyboardShortcut.Keys.Enter.code,
+            WebInspector.KeyboardShortcut.Keys.Esc.code,
+        ].indexOf(keyCode) >= 0;
+    },
+
+    /**
+     * @override
+     */
+    willHide: function()
+    {
+        this._onHide();
+        WebInspector.HelpScreen.prototype.willHide.call(this);
+    },
+
+    __proto__: WebInspector.HelpScreen.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {string} name
+ * @param {string=} id
+ */
+WebInspector.SettingsTab = function(name, id)
+{
+    WebInspector.View.call(this);
+    this.element.className = "settings-tab-container";
+    if (id)
+        this.element.id = id;
+    var header = this.element.createChild("header");
+    header.createChild("h3").appendChild(document.createTextNode(name));
+    this.containerElement = this.element.createChild("div", "help-container-wrapper").createChild("div", "settings-tab help-content help-container");
+}
+
+WebInspector.SettingsTab.prototype = {
+    /**
+     *  @param {string=} name
+     *  @return {!Element}
+     */
+    _appendSection: function(name)
+    {
+        var block = this.containerElement.createChild("div", "help-block");
+        if (name)
+            block.createChild("div", "help-section-title").textContent = name;
+        return block;
+    },
+
+    /**
+     * @param {boolean=} omitParagraphElement
+     * @param {Element=} inputElement
+     */
+    _createCheckboxSetting: function(name, setting, omitParagraphElement, inputElement)
+    {
+        var input = inputElement || document.createElement("input");
+        input.type = "checkbox";
+        input.name = name;
+        input.checked = setting.get();
+
+        function listener()
+        {
+            setting.set(input.checked);
+        }
+        input.addEventListener("click", listener, false);
+
+        var label = document.createElement("label");
+        label.appendChild(input);
+        label.appendChild(document.createTextNode(name));
+        if (omitParagraphElement)
+            return label;
+
+        var p = document.createElement("p");
+        p.appendChild(label);
+        return p;
+    },
+
+    _createSelectSetting: function(name, options, setting)
+    {
+        var fieldsetElement = document.createElement("fieldset");
+        fieldsetElement.createChild("label").textContent = name;
+
+        var select = document.createElement("select");
+        var settingValue = setting.get();
+
+        for (var i = 0; i < options.length; ++i) {
+            var option = options[i];
+            select.add(new Option(option[0], option[1]));
+            if (settingValue === option[1])
+                select.selectedIndex = i;
+        }
+
+        function changeListener(e)
+        {
+            setting.set(e.target.value);
+        }
+
+        select.addEventListener("change", changeListener, false);
+        fieldsetElement.appendChild(select);
+
+        var p = document.createElement("p");
+        p.appendChild(fieldsetElement);
+        return p;
+    },
+
+    _createRadioSetting: function(name, options, setting)
+    {
+        var pp = document.createElement("p");
+        var fieldsetElement = document.createElement("fieldset");
+        var legendElement = document.createElement("legend");
+        legendElement.textContent = name;
+        fieldsetElement.appendChild(legendElement);
+
+        function clickListener(e)
+        {
+            setting.set(e.target.value);
+        }
+
+        var settingValue = setting.get();
+        for (var i = 0; i < options.length; ++i) {
+            var p = document.createElement("p");
+            var label = document.createElement("label");
+            p.appendChild(label);
+
+            var input = document.createElement("input");
+            input.type = "radio";
+            input.name = setting.name;
+            input.value = options[i][0];
+            input.addEventListener("click", clickListener, false);
+            if (settingValue == input.value)
+                input.checked = true;
+
+            label.appendChild(input);
+            label.appendChild(document.createTextNode(options[i][1]));
+
+            fieldsetElement.appendChild(p);
+        }
+
+        pp.appendChild(fieldsetElement);
+        return pp;
+    },
+
+    /**
+     * @param {string} label
+     * @param {WebInspector.Setting} setting
+     * @param {boolean} numeric
+     * @param {number=} maxLength
+     * @param {string=} width
+     * @param {function(string):boolean=} validatorCallback
+     */
+    _createInputSetting: function(label, setting, numeric, maxLength, width, validatorCallback)
+    {
+        var fieldset = document.createElement("fieldset");
+        var p = fieldset.createChild("p");
+        var labelElement = p.createChild("label");
+        labelElement.textContent = label + " ";
+        var inputElement = labelElement.createChild("input");
+        inputElement.value = setting.get();
+        inputElement.type = "text";
+        if (numeric)
+            inputElement.className = "numeric";
+        if (maxLength)
+            inputElement.maxLength = maxLength;
+        if (width)
+            inputElement.style.width = width;
+
+        function onBlur()
+        {
+            if (validatorCallback && !validatorCallback(inputElement.value)) {
+                inputElement.value = setting.get();
+                return;
+            }
+            setting.set(numeric ? Number(inputElement.value) : inputElement.value);
+        }
+        inputElement.addEventListener("blur", onBlur, false);
+        return fieldset;
+    },
+
+    _createCustomSetting: function(name, element)
+    {
+        var p = document.createElement("p");
+        var fieldsetElement = document.createElement("fieldset");
+        fieldsetElement.createChild("label").textContent = name;
+        fieldsetElement.appendChild(element);
+        p.appendChild(fieldsetElement);
+        return p;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SettingsTab}
+ */
+WebInspector.GenericSettingsTab = function()
+{
+    WebInspector.SettingsTab.call(this, WebInspector.UIString("General"), "general-tab-content");
+
+    var p = this._appendSection();
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Disable cache (while DevTools is open)"), WebInspector.settings.cacheDisabled));
+    var disableJSElement = this._createCheckboxSetting(WebInspector.UIString("Disable JavaScript"), WebInspector.settings.javaScriptDisabled);
+    p.appendChild(disableJSElement);
+    WebInspector.settings.javaScriptDisabled.addChangeListener(this._javaScriptDisabledChanged, this);
+    this._disableJSCheckbox = disableJSElement.getElementsByTagName("input")[0];
+    this._updateScriptDisabledCheckbox();
+
+    p = this._appendSection(WebInspector.UIString("Appearance"));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show toolbar icons"), WebInspector.settings.showToolbarIcons));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Split panels vertically when docked to right"), WebInspector.settings.splitVerticallyWhenDockedToRight));
+
+    p = this._appendSection(WebInspector.UIString("Elements"));
+    p.appendChild(this._createRadioSetting(WebInspector.UIString("Color format"), [
+        [ WebInspector.Color.Format.Original, WebInspector.UIString("As authored") ],
+        [ WebInspector.Color.Format.HEX, "HEX: #DAC0DE" ],
+        [ WebInspector.Color.Format.RGB, "RGB: rgb(128, 255, 255)" ],
+        [ WebInspector.Color.Format.HSL, "HSL: hsl(300, 80%, 90%)" ] ], WebInspector.settings.colorFormat));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show user agent styles"), WebInspector.settings.showUserAgentStyles));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Word wrap"), WebInspector.settings.domWordWrap));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show Shadow DOM"), WebInspector.settings.showShadowDOM));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show rulers"), WebInspector.settings.showMetricsRulers));
+
+    p = this._appendSection(WebInspector.UIString("Rendering"));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show paint rectangles"), WebInspector.settings.showPaintRects));
+    WebInspector.settings.showPaintRects.addChangeListener(this._showPaintRectsChanged, this);
+
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show composited layer borders"), WebInspector.settings.showDebugBorders));
+    WebInspector.settings.showDebugBorders.addChangeListener(this._showDebugBordersChanged, this);
+
+    if (Capabilities.canShowFPSCounter) {
+        p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show FPS meter"), WebInspector.settings.showFPSCounter));
+        WebInspector.settings.showFPSCounter.addChangeListener(this._showFPSCounterChanged, this);
+    }
+    if (Capabilities.canContinuouslyPaint) {
+        p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Enable continuous page repainting"), WebInspector.settings.continuousPainting));
+        WebInspector.settings.continuousPainting.addChangeListener(this._continuousPaintingChanged, this);
+    }
+
+    p = this._appendSection(WebInspector.UIString("Sources"));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Search in content scripts"), WebInspector.settings.searchInContentScripts));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Enable source maps"), WebInspector.settings.sourceMapsEnabled));
+    if (WebInspector.experimentsSettings.isEnabled("sass"))
+        p.appendChild(this._createCSSAutoReloadControls());
+    var indentationElement = this._createSelectSetting(WebInspector.UIString("Indentation"), [
+            [ WebInspector.UIString("2 spaces"), WebInspector.TextUtils.Indent.TwoSpaces ],
+            [ WebInspector.UIString("4 spaces"), WebInspector.TextUtils.Indent.FourSpaces ],
+            [ WebInspector.UIString("8 spaces"), WebInspector.TextUtils.Indent.EightSpaces ],
+            [ WebInspector.UIString("Tab character"), WebInspector.TextUtils.Indent.TabCharacter ]
+        ], WebInspector.settings.textEditorIndent);
+    indentationElement.firstChild.className = "toplevel";
+    p.appendChild(indentationElement);
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Use CodeMirror editor"), WebInspector.settings.codemirror));
+
+    p = this._appendSection(WebInspector.UIString("Profiler"));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show objects' hidden properties"), WebInspector.settings.showHeapSnapshotObjectsHiddenProperties));
+    if (WebInspector.experimentsSettings.nativeMemorySnapshots.isEnabled())
+        p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show uninstrumented native memory"), WebInspector.settings.showNativeSnapshotUninstrumentedSize));
+
+    p = this._appendSection(WebInspector.UIString("Timeline"));
+    var checkbox = this._createCheckboxSetting(WebInspector.UIString("Limit number of captured JS stack frames"), WebInspector.settings.timelineLimitStackFramesFlag);
+    p.appendChild(checkbox);
+    var fieldset = this._createInputSetting(WebInspector.UIString("Frames to capture"), WebInspector.settings.timelineStackFramesToCapture, true, 2, "2em");
+    fieldset.disabled = !WebInspector.settings.timelineLimitStackFramesFlag.get();
+    WebInspector.settings.timelineLimitStackFramesFlag.addChangeListener(this._timelineLimitStackFramesChanged.bind(this, fieldset));
+    checkbox.appendChild(fieldset);
+
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show CPU activity on the ruler"), WebInspector.settings.showCpuOnTimelineRuler));
+
+    p = this._appendSection(WebInspector.UIString("Console"));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Log XMLHttpRequests"), WebInspector.settings.monitoringXHREnabled));
+    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Preserve log upon navigation"), WebInspector.settings.preserveConsoleLog));
+
+    if (WebInspector.extensionServer.hasExtensions()) {
+        var handlerSelector = new WebInspector.HandlerSelector(WebInspector.openAnchorLocationRegistry);
+        p = this._appendSection(WebInspector.UIString("Extensions"));
+        p.appendChild(this._createCustomSetting(WebInspector.UIString("Open links in"), handlerSelector.element));
+    }
+
+    p = this._appendSection();
+    var panelShortcutTitle = WebInspector.UIString("Enable %s + 1-9 shortcut to switch panels", WebInspector.isMac() ? "Cmd" : "Ctrl");
+    p.appendChild(this._createCheckboxSetting(panelShortcutTitle, WebInspector.settings.shortcutPanelSwitch));
+}
+
+WebInspector.GenericSettingsTab.prototype = {
+    _showPaintRectsChanged: function()
+    {
+        PageAgent.setShowPaintRects(WebInspector.settings.showPaintRects.get());
+    },
+
+    _showDebugBordersChanged: function()
+    {
+        PageAgent.setShowDebugBorders(WebInspector.settings.showDebugBorders.get());
+    },
+
+    _showFPSCounterChanged: function()
+    {
+        PageAgent.setShowFPSCounter(WebInspector.settings.showFPSCounter.get());
+    },
+
+    _continuousPaintingChanged: function()
+    {
+        PageAgent.setContinuousPaintingEnabled(WebInspector.settings.continuousPainting.get());
+    },
+
+    /**
+     * @param {HTMLFieldSetElement} fieldset
+     */
+    _timelineLimitStackFramesChanged: function(fieldset)
+    {
+        fieldset.disabled = !WebInspector.settings.timelineLimitStackFramesFlag.get();
+    },
+
+    _updateScriptDisabledCheckbox: function()
+    {
+        function executionStatusCallback(error, status)
+        {
+            if (error || !status)
+                return;
+
+            switch (status) {
+            case "forbidden":
+                this._disableJSCheckbox.checked = true;
+                this._disableJSCheckbox.disabled = true;
+                break;
+            case "disabled":
+                this._disableJSCheckbox.checked = true;
+                break;
+            default:
+                this._disableJSCheckbox.checked = false;
+                break;
+            }
+        }
+
+        PageAgent.getScriptExecutionStatus(executionStatusCallback.bind(this));
+    },
+
+    _javaScriptDisabledChanged: function()
+    {
+        // We need to manually update the checkbox state, since enabling JavaScript in the page can actually uncover the "forbidden" state.
+        PageAgent.setScriptExecutionDisabled(WebInspector.settings.javaScriptDisabled.get(), this._updateScriptDisabledCheckbox.bind(this));
+    },
+
+    _createCSSAutoReloadControls: function()
+    {
+        var fragment = document.createDocumentFragment();
+        var labelElement = fragment.createChild("label");
+        var checkboxElement = labelElement.createChild("input");
+        checkboxElement.type = "checkbox";
+        checkboxElement.checked = WebInspector.settings.cssReloadEnabled.get();
+        checkboxElement.addEventListener("click", checkboxClicked, false);
+        labelElement.appendChild(document.createTextNode(WebInspector.UIString("Auto-reload CSS upon Sass save")));
+
+        var fieldsetElement = this._createInputSetting(WebInspector.UIString("Timeout (ms)"), WebInspector.settings.cssReloadTimeout, true, 8, "60px", validateReloadTimeout);
+        fieldsetElement.disabled = !checkboxElement.checked;
+        fragment.appendChild(fieldsetElement);
+        return fragment;
+
+        function checkboxClicked()
+        {
+            var reloadEnabled = checkboxElement.checked;
+            WebInspector.settings.cssReloadEnabled.set(reloadEnabled);
+            fieldsetElement.disabled = !reloadEnabled;
+        }
+
+        function validateReloadTimeout(value)
+        {
+            return isFinite(value) && value > 0;
+        }
+    },
+
+    __proto__: WebInspector.SettingsTab.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SettingsTab}
+ */
+WebInspector.OverridesSettingsTab = function()
+{
+    WebInspector.SettingsTab.call(this, WebInspector.UIString("Overrides"), "overrides-tab-content");
+    this._view = new WebInspector.OverridesView();
+    this.containerElement.parentElement.appendChild(this._view.containerElement);
+    this.containerElement.removeSelf();
+    this.containerElement = this._view.containerElement;
+}
+
+WebInspector.OverridesSettingsTab.prototype = {
+    __proto__: WebInspector.SettingsTab.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SettingsTab}
+ */
+WebInspector.WorkspaceSettingsTab = function()
+{
+    WebInspector.SettingsTab.call(this, WebInspector.UIString("Workspace"), "workspace-tab-content");
+    this._reset();
+}
+
+WebInspector.WorkspaceSettingsTab.prototype = {
+    wasShown: function()
+    {
+        WebInspector.SettingsTab.prototype.wasShown.call(this);
+        this._reset();
+    },
+
+    _reset: function()
+    {
+        this.containerElement.removeChildren();
+        this._createFileSystemsEditor();
+        this._createFileMappingEditor();
+    },
+
+    _createFileSystemsEditor: function()
+    {
+        var p = this._appendSection(WebInspector.UIString("File systems"));
+        this._fileSystemsEditor = p.createChild("p", "file-systems-editor");
+
+        this._addFileSystemRowElement = this._fileSystemsEditor.createChild("div", "workspace-settings-row");
+        var addFileSystemButton = this._addFileSystemRowElement.createChild("input", "file-system-add-button");
+        addFileSystemButton.type = "button";
+        addFileSystemButton.value = WebInspector.UIString("Add file system");
+        addFileSystemButton.addEventListener("click", this._addFileSystemClicked.bind(this));
+
+        var fileSystemPaths = WebInspector.isolatedFileSystemManager.mapping().fileSystemPaths();
+        for (var i = 0; i < fileSystemPaths.length; ++i)
+            this._addFileSystemRow(fileSystemPaths[i]);
+
+        return this._fileSystemsEditor;
+    },
+
+    /**
+     * @return {Element}
+     */
+    _createShowTextInput: function(className, value)
+    {
+        var inputElement = document.createElement("input");
+        inputElement.addStyleClass(className);
+        inputElement.type = "text";
+        inputElement.value = value;
+        inputElement.title = value;
+        inputElement.disabled = true;
+        return inputElement;
+    },
+
+    /**
+     * @return {Element}
+     */
+    _createEditTextInput: function(className, placeHolder)
+    {
+        var inputElement = document.createElement("input");
+        inputElement.addStyleClass(className);
+        inputElement.type = "text";
+        inputElement.placeholder = placeHolder;
+        return inputElement;
+    },
+
+    /**
+     * @param {function(Event)} handler
+     * @return {Element}
+     */
+    _createRemoveButton: function(handler)
+    {
+        var removeButton = document.createElement("button");
+        removeButton.addStyleClass("button");
+        removeButton.addStyleClass("remove-button");
+        removeButton.value = WebInspector.UIString("Remove");
+        removeButton.addEventListener("click", handler, false);
+        return removeButton;
+    },
+
+    /**
+     * @param {function(Event)} handler
+     * @return {Element}
+     */
+    _createAddButton: function(handler)
+    {
+        var addButton = document.createElement("button");
+        addButton.addStyleClass("button");
+        addButton.addStyleClass("add-button");
+        addButton.value = WebInspector.UIString("Add");
+        addButton.addEventListener("click", handler, false);
+        return addButton;
+    },
+
+    /**
+     * @param {string} fileSystemPath
+     */
+    _addFileSystemRow: function(fileSystemPath)
+    {
+        var fileSystemRow = document.createElement("div");
+        fileSystemRow.addStyleClass("workspace-settings-row");
+        fileSystemRow.addStyleClass("file-system-row");
+        this._fileSystemsEditor.insertBefore(fileSystemRow, this._addFileSystemRowElement);
+
+        fileSystemRow.appendChild(this._createShowTextInput("file-system-path", fileSystemPath));
+        var removeFileSystemButton = this._createRemoveButton(removeFileSystemClicked.bind(this));
+        fileSystemRow.appendChild(removeFileSystemButton);
+
+        function removeFileSystemClicked()
+        {
+            removeFileSystemButton.disabled = true;
+            WebInspector.isolatedFileSystemManager.removeFileSystem(fileSystemPath, fileSystemRemoved.bind(this));
+        }
+
+        function fileSystemRemoved()
+        {
+            this._fileSystemsEditor.removeChild(fileSystemRow);
+            removeFileSystemButton.disabled = false;
+        }
+    },
+
+    _addFileSystemClicked: function()
+    {
+        WebInspector.isolatedFileSystemManager.addFileSystem(this._fileSystemAdded.bind(this));
+    },
+
+    /**
+     * @param {?string} fileSystemPath
+     */
+    _fileSystemAdded: function(fileSystemPath)
+    {
+        if (fileSystemPath)
+            this._addFileSystemRow(fileSystemPath);
+    },
+
+    _createFileMappingEditor: function()
+    {
+        var p = this._appendSection(WebInspector.UIString("Mappings"));
+        this._fileMappingEditor = p.createChild("p", "file-mappings-editor");
+
+        this._addMappingRowElement = this._fileMappingEditor.createChild("div", "workspace-settings-row");
+
+        this._urlInputElement = this._createEditTextInput("file-mapping-url", WebInspector.UIString("File mapping url"));
+        this._addMappingRowElement.appendChild(this._urlInputElement);
+        this._pathInputElement = this._createEditTextInput("file-mapping-path", WebInspector.UIString("File mapping path"));
+        this._addMappingRowElement.appendChild(this._pathInputElement);
+
+        this._addMappingRowElement.appendChild(this._createAddButton(this._addFileMappingClicked.bind(this)));
+
+        var mappingEntries = WebInspector.fileMapping.mappingEntries();
+        for (var i = 0; i < mappingEntries.length; ++i)
+            this._addMappingRow(mappingEntries[i]);
+
+        return this._fileMappingEditor;
+    },
+
+    /**
+     * @param {WebInspector.FileMapping.Entry} mappingEntry
+     */
+    _addMappingRow: function(mappingEntry)
+    {
+        var fileMappingRow = document.createElement("div");
+        fileMappingRow.addStyleClass("workspace-settings-row");
+        this._fileMappingEditor.insertBefore(fileMappingRow, this._addMappingRowElement);
+
+        fileMappingRow.appendChild(this._createShowTextInput("file-mapping-url", mappingEntry.urlPrefix));
+        fileMappingRow.appendChild(this._createShowTextInput("file-mapping-path", mappingEntry.pathPrefix));
+
+        fileMappingRow.appendChild(this._createRemoveButton(removeMappingClicked.bind(this)));
+
+        function removeMappingClicked()
+        {
+            var index = Array.prototype.slice.call(fileMappingRow.parentElement.childNodes).indexOf(fileMappingRow);
+            var mappingEntries = WebInspector.fileMapping.mappingEntries();
+            mappingEntries.splice(index, 1);
+            WebInspector.fileMapping.setMappingEntries(mappingEntries);
+            this._fileMappingEditor.removeChild(fileMappingRow);
+        }
+    },
+
+    _addFileMappingClicked: function()
+    {
+        var url = this._urlInputElement.value;
+        var path = this._pathInputElement.value;
+        if (!url || !path)
+            return;
+        var mappingEntries = WebInspector.fileMapping.mappingEntries();
+        if (url[url.length - 1] !== "/")
+            url += "/";
+        if (path[path.length - 1] !== "/")
+            path += "/";
+        var mappingEntry = new WebInspector.FileMapping.Entry(url, path);
+        mappingEntries.push(mappingEntry);
+        WebInspector.fileMapping.setMappingEntries(mappingEntries);
+        this._addMappingRow(mappingEntry);
+        this._urlInputElement.value = "";
+        this._pathInputElement.value = "";
+    },
+
+    __proto__: WebInspector.SettingsTab.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SettingsTab}
+ */
+WebInspector.TetheringSettingsTab = function()
+{
+    WebInspector.SettingsTab.call(this, WebInspector.UIString("Port Forwarding"), "workspace-tab-content");
+}
+
+WebInspector.TetheringSettingsTab.prototype = {
+    wasShown: function()
+    {
+        if (this._paragraphElement)
+            return;
+
+        this._paragraphElement = this._appendSection(WebInspector.UIString("Mappings"));
+        WebInspector.SettingsTab.prototype.wasShown.call(this);
+        var mappingEntries = WebInspector.settings.portForwardings.get();
+        for (var i = 0; i < mappingEntries.length; ++i)
+            this._addMappingRow(mappingEntries[i].port, mappingEntries[i].location, false);
+        if (!mappingEntries.length)
+            this._addMappingRow("", "", true);
+        this._save();
+    },
+
+    /**
+     * @param {string} port
+     * @param {string} location
+     * @param {boolean} focus
+     * @return {Element}
+     */
+    _addMappingRow: function(port, location, focus)
+    {
+        var mappingRow = this._paragraphElement.createChild("div", "workspace-settings-row");
+        var portElement = mappingRow.createChild("input");
+        portElement.type = "text";
+        portElement.value = port || "";
+        if (!port)
+            portElement.placeholder = "8080";
+        portElement.addEventListener("keydown", this._editTextInputKey.bind(this, true), true);
+        portElement.addEventListener("blur", this._save.bind(this), true);
+        portElement.addEventListener("input", this._validatePort.bind(this, portElement), true);
+
+        var locationElement = mappingRow.createChild("input");
+        locationElement.type = "text";
+        locationElement.value = location || "127.0.0.1:";
+        locationElement.addEventListener("keydown", this._editTextInputKey.bind(this, false), true);
+        locationElement.addEventListener("blur", this._save.bind(this), true);
+        locationElement.addEventListener("input", this._validateLocation.bind(this, locationElement), true);
+
+        var removeButton = mappingRow.createChild("button", "button remove-button");
+        removeButton.value = WebInspector.UIString("Remove");
+        removeButton.tabIndex = -1;
+        removeButton.addEventListener("click", removeMappingClicked.bind(this), false);
+
+        function removeMappingClicked()
+        {
+            mappingRow.removeSelf();
+            if (!this._paragraphElement.querySelector(".workspace-settings-row"))
+                this._addMappingRow();
+            this._save();
+        }
+        if (focus)
+            setTimeout(function() { portElement.focus(); }, 0); // Needed to work on wasShown
+        return mappingRow;
+    },
+
+    _save: function()
+    {
+        var portForwardings = [];
+        for (var rowElement = this._paragraphElement.firstChild.nextSibling; rowElement; rowElement = rowElement.nextSibling) {
+            var portElement = rowElement.firstChild;
+            var locationElement = portElement.nextSibling;
+            var port = this._validatePort(portElement);
+            var location = this._validateLocation(locationElement);
+            if (!port || !location)
+                continue;
+            portForwardings.push({ port : parseInt(port, 10), location : location });
+        }
+        WebInspector.settings.portForwardings.set(portForwardings);
+    },
+
+    /**
+     * @param {boolean} isPort
+     * @param {Event} event
+     */
+    _editTextInputKey: function(isPort, event)
+    {
+        if (!WebInspector.KeyboardShortcut.hasNoModifiers(/** @type {KeyboardEvent}*/ (event)))
+            return;
+
+        if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Enter.code ||
+            event.keyCode === WebInspector.KeyboardShortcut.Keys.Tab.code) {
+            if (isPort)
+                event.target.nextElementSibling.focus();
+            else {
+                if (event.target.parentElement.nextSibling)
+                    event.target.parentElement.nextSibling.firstChild.focus();
+                else
+                    this._addMappingRow("", "", true);
+            }
+            event.consume(true);
+        }
+    },
+
+    /**
+     * @param {Element} element
+     * @param {Event=} event
+     * @return {number}
+     */
+    _validatePort: function(element, event)
+    {
+        var port = element.value;
+        if (isNaN(port) || port < 5000 || port > 10000) {
+            element.addStyleClass("workspace-settings-error");
+            return 0;
+        }
+        element.removeStyleClass("workspace-settings-error");
+        return parseInt(port, 10);
+    },
+
+    /**
+     * @param {Element} element
+     * @param {Event=} event
+     * @return {string}
+     */
+    _validateLocation: function(element, event)
+    {
+        var location = element.value;
+        if (!/.*:\d+/.test(location)) {
+            element.addStyleClass("workspace-settings-error");
+            return "";
+        }
+        element.removeStyleClass("workspace-settings-error");
+        return location;
+    },
+
+    __proto__: WebInspector.SettingsTab.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SettingsTab}
+ */
+WebInspector.ExperimentsSettingsTab = function()
+{
+    WebInspector.SettingsTab.call(this, WebInspector.UIString("Experiments"), "experiments-tab-content");
+
+    var experiments = WebInspector.experimentsSettings.experiments;
+    if (experiments.length) {
+        var experimentsSection = this._appendSection();
+        experimentsSection.appendChild(this._createExperimentsWarningSubsection());
+        for (var i = 0; i < experiments.length; ++i)
+            experimentsSection.appendChild(this._createExperimentCheckbox(experiments[i]));
+    }
+}
+
+WebInspector.ExperimentsSettingsTab.prototype = {
+    /**
+     * @return {Element} element
+     */
+    _createExperimentsWarningSubsection: function()
+    {
+        var subsection = document.createElement("div");
+        var warning = subsection.createChild("span", "settings-experiments-warning-subsection-warning");
+        warning.textContent = WebInspector.UIString("WARNING:");
+        subsection.appendChild(document.createTextNode(" "));
+        var message = subsection.createChild("span", "settings-experiments-warning-subsection-message");
+        message.textContent = WebInspector.UIString("These experiments could be dangerous and may require restart.");
+        return subsection;
+    },
+
+    _createExperimentCheckbox: function(experiment)
+    {
+        var input = document.createElement("input");
+        input.type = "checkbox";
+        input.name = experiment.name;
+        input.checked = experiment.isEnabled();
+        function listener()
+        {
+            experiment.setEnabled(input.checked);
+        }
+        input.addEventListener("click", listener, false);
+
+        var p = document.createElement("p");
+        var label = document.createElement("label");
+        label.appendChild(input);
+        label.appendChild(document.createTextNode(WebInspector.UIString(experiment.title)));
+        p.appendChild(label);
+        return p;
+    },
+
+    __proto__: WebInspector.SettingsTab.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.SettingsController = function()
+{
+    this._statusBarButton = new WebInspector.StatusBarButton(WebInspector.UIString("Settings"), "settings-status-bar-item");
+    if (WebInspector.experimentsSettings.showOverridesInDrawer.isEnabled())
+        this._statusBarButton.element.addEventListener("mousedown", this._mouseDown.bind(this), false);
+    else
+        this._statusBarButton.element.addEventListener("mouseup", this._mouseUp.bind(this), false);
+
+    /** @type {?WebInspector.SettingsScreen} */
+    this._settingsScreen;
+}
+
+WebInspector.SettingsController.prototype =
+{
+    get statusBarItem()
+    {
+        return this._statusBarButton.element;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _mouseDown: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString("Overrides"), showOverrides.bind(this));
+        contextMenu.appendItem(WebInspector.UIString("Settings"), showSettings.bind(this));
+
+        function showOverrides()
+        {
+            if (this._settingsScreenVisible)
+                this._hideSettingsScreen();
+            WebInspector.OverridesView.showInDrawer();
+        }
+
+        function showSettings()
+        {
+            if (!this._settingsScreenVisible)
+                this.showSettingsScreen();
+        }
+
+        contextMenu.showSoftMenu();
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _mouseUp: function(event)
+    {
+        this.showSettingsScreen();
+    },
+
+    _onHideSettingsScreen: function()
+    {
+        delete this._settingsScreenVisible;
+    },
+
+    /**
+     * @param {string=} tabId
+     */
+    showSettingsScreen: function(tabId)
+    {
+        if (!this._settingsScreen)
+            this._settingsScreen = new WebInspector.SettingsScreen(this._onHideSettingsScreen.bind(this));
+
+        if (tabId)
+            this._settingsScreen.selectTab(tabId);
+
+        this._settingsScreen.showModal();
+        this._settingsScreenVisible = true;
+    },
+
+    _hideSettingsScreen: function()
+    {
+        if (this._settingsScreen)
+            this._settingsScreen.hide();
+    },
+
+    resize: function()
+    {
+        if (this._settingsScreen && this._settingsScreen.isShowing())
+            this._settingsScreen.doResize();
+    }
+}
diff --git a/Source/devtools/front_end/ShortcutsScreen.js b/Source/devtools/front_end/ShortcutsScreen.js
new file mode 100644
index 0000000..a136c30
--- /dev/null
+++ b/Source/devtools/front_end/ShortcutsScreen.js
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.ShortcutsScreen = function()
+{
+    this._sections = /** @type {Object.<string, !WebInspector.ShortcutsSection>} */ ({});
+}
+
+WebInspector.ShortcutsScreen.prototype = {
+    /**
+     * @param {string} name
+     * @return {!WebInspector.ShortcutsSection}
+     */
+    section: function(name)
+    {
+        var section = this._sections[name];
+        if (!section)
+            this._sections[name] = section = new WebInspector.ShortcutsSection(name);
+        return section;
+    },
+
+    /**
+     * @return {!WebInspector.View}
+     */
+    createShortcutsTabView: function()
+    {
+        var orderedSections = [];
+        for (var section in this._sections)
+            orderedSections.push(this._sections[section]);
+        function compareSections(a, b)
+        {
+            return a.order - b.order;
+        }
+        orderedSections.sort(compareSections);
+
+        var view = new WebInspector.View();
+
+        view.element.className = "settings-tab-container";
+        view.element.createChild("header").createChild("h3").appendChild(document.createTextNode(WebInspector.UIString("Shortcuts")));
+        var container = view.element.createChild("div", "help-container-wrapper").createChild("div");
+        container.className = "help-content help-container";
+        for (var i = 0; i < orderedSections.length; ++i)
+            orderedSections[i].renderSection(container);
+
+        return view;
+    }
+}
+
+/**
+ * We cannot initialize it here as localized strings are not loaded yet.
+ * @type {?WebInspector.ShortcutsScreen}
+ */
+WebInspector.shortcutsScreen = null;
+
+/**
+ * @constructor
+ * @param {string} name
+ */
+WebInspector.ShortcutsSection = function(name)
+{
+    this.name = name;
+    this._lines = /** @type {!Array.<{key: !Node, text: string}>} */ ([]);
+    this.order = ++WebInspector.ShortcutsSection._sequenceNumber;
+};
+
+WebInspector.ShortcutsSection._sequenceNumber = 0;
+
+WebInspector.ShortcutsSection.prototype = {
+    /**
+     * @param {!WebInspector.KeyboardShortcut.Descriptor} key
+     * @param {string} description
+     */
+    addKey: function(key, description)
+    {
+        this._addLine(this._renderKey(key), description);
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} keys
+     * @param {string} description
+     */
+    addRelatedKeys: function(keys, description)
+    {
+        this._addLine(this._renderSequence(keys, "/"), description);
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} keys
+     * @param {string} description
+     */
+    addAlternateKeys: function(keys, description)
+    {
+        this._addLine(this._renderSequence(keys, WebInspector.UIString("or")), description);
+    },
+
+    /**
+     * @param {!Node} keyElement
+     * @param {string} description
+     */
+    _addLine: function(keyElement, description)
+    {
+        this._lines.push({ key: keyElement, text: description })
+    },
+
+    /**
+     * @param {!Element} container
+     */
+    renderSection: function(container)
+    {
+        var parent = container.createChild("div", "help-block");
+
+        var headLine = parent.createChild("div", "help-line");
+        headLine.createChild("div", "help-key-cell");
+        headLine.createChild("div", "help-section-title help-cell").textContent = this.name;
+
+        for (var i = 0; i < this._lines.length; ++i) {
+            var line = parent.createChild("div", "help-line");
+            var keyCell = line.createChild("div", "help-key-cell");
+            keyCell.appendChild(this._lines[i].key);
+            keyCell.appendChild(this._createSpan("help-key-delimiter", ":"));
+            line.createChild("div", "help-cell").textContent = this._lines[i].text;
+        }
+    },
+
+    /**
+     * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} sequence
+     * @param {string} delimiter
+     * @return {!Node}
+     */
+    _renderSequence: function(sequence, delimiter)
+    {
+        var delimiterSpan = this._createSpan("help-key-delimiter", delimiter);
+        return this._joinNodes(sequence.map(this._renderKey.bind(this)), delimiterSpan);
+    },
+
+    /**
+     * @param {!WebInspector.KeyboardShortcut.Descriptor} key
+     * @return {!Node}
+     */
+    _renderKey: function(key)
+    {
+        var keyName = key.name;
+        var plus = this._createSpan("help-combine-keys", "+");
+        return this._joinNodes(keyName.split(" + ").map(this._createSpan.bind(this, "help-key monospace")), plus);
+    },
+
+    /**
+     * @param {string} className
+     * @param {string} textContent
+     * @return {!Element}
+     */
+    _createSpan: function(className, textContent)
+    {
+        var node = document.createElement("span");
+        node.className = className;
+        node.textContent = textContent;
+        return node;
+    },
+
+    /**
+     * @param {!Array.<!Element>} nodes
+     * @param {!Element} delimiter
+     * @return {!Node}
+     */
+    _joinNodes: function(nodes, delimiter)
+    {
+        var result = document.createDocumentFragment();
+        for (var i = 0; i < nodes.length; ++i) {
+            if (i > 0)
+                result.appendChild(delimiter.cloneNode(true));
+            result.appendChild(nodes[i]);
+        }
+        return result;
+    }
+}
diff --git a/Source/devtools/front_end/ShowMoreDataGridNode.js b/Source/devtools/front_end/ShowMoreDataGridNode.js
new file mode 100644
index 0000000..7914a1b
--- /dev/null
+++ b/Source/devtools/front_end/ShowMoreDataGridNode.js
@@ -0,0 +1,147 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.DataGridNode}
+ * @param {function(number, number)} callback
+ * @param {number} startPosition
+ * @param {number} endPosition
+ * @param {number} chunkSize
+ */
+WebInspector.ShowMoreDataGridNode = function(callback, startPosition, endPosition, chunkSize)
+{
+    WebInspector.DataGridNode.call(this, {summaryRow:true}, false);
+    this._callback = callback;
+    this._startPosition = startPosition;
+    this._endPosition = endPosition;
+    this._chunkSize = chunkSize;
+
+    this.showNext = document.createElement("button");
+    this.showNext.setAttribute("type", "button");
+    this.showNext.addEventListener("click", this._showNextChunk.bind(this), false);
+    this.showNext.textContent = WebInspector.UIString("Show %d before", this._chunkSize);
+
+    this.showAll = document.createElement("button");
+    this.showAll.setAttribute("type", "button");
+    this.showAll.addEventListener("click", this._showAll.bind(this), false);
+
+    this.showLast = document.createElement("button");
+    this.showLast.setAttribute("type", "button");
+    this.showLast.addEventListener("click", this._showLastChunk.bind(this), false);
+    this.showLast.textContent = WebInspector.UIString("Show %d after", this._chunkSize);
+
+    this._updateLabels();
+    this.selectable = false;
+}
+
+WebInspector.ShowMoreDataGridNode.prototype = {
+    _showNextChunk: function()
+    {
+        this._callback(this._startPosition, this._startPosition + this._chunkSize);
+    },
+
+    _showAll: function()
+    {
+        this._callback(this._startPosition, this._endPosition);
+    },
+
+    _showLastChunk: function()
+    {
+        this._callback(this._endPosition - this._chunkSize, this._endPosition);
+    },
+
+    _updateLabels: function()
+    {
+        var totalSize = this._endPosition - this._startPosition;
+        if (totalSize > this._chunkSize) {
+            this.showNext.removeStyleClass("hidden");
+            this.showLast.removeStyleClass("hidden");
+        } else {
+            this.showNext.addStyleClass("hidden");
+            this.showLast.addStyleClass("hidden");
+        }
+        this.showAll.textContent = WebInspector.UIString("Show all %d", totalSize);
+    },
+
+    createCells: function()
+    {
+        var cell = document.createElement("td");
+        if (this.depth)
+            cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px");
+        cell.appendChild(this.showNext);
+        cell.appendChild(this.showAll);
+        cell.appendChild(this.showLast);
+        this._element.appendChild(cell);
+
+        var columns = this.dataGrid.columns;
+        var count = 0;
+        for (var c in columns)
+            ++count;
+        while (--count > 0) {
+            cell = document.createElement("td");
+            this._element.appendChild(cell);
+        }
+    },
+
+    /**
+     * @param {number} from
+     */
+    setStartPosition: function(from)
+    {
+        this._startPosition = from;
+        this._updateLabels();
+    },
+
+    /**
+     * @param {number} to
+     */
+    setEndPosition: function(to)
+    {
+        this._endPosition = to;
+        this._updateLabels();
+    },
+
+    /**
+     * @override
+     * @return {number}
+     */
+    nodeHeight: function()
+    {
+        return 32;
+    },
+
+    dispose: function()
+    {
+    },
+
+    __proto__: WebInspector.DataGridNode.prototype
+}
+
diff --git a/Source/devtools/front_end/SidebarOverlay.js b/Source/devtools/front_end/SidebarOverlay.js
new file mode 100644
index 0000000..6a9f56f
--- /dev/null
+++ b/Source/devtools/front_end/SidebarOverlay.js
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.View} view
+ * @param {string} widthSettingName
+ * @param {number} minimalWidth
+ */
+WebInspector.SidebarOverlay = function(view, widthSettingName, minimalWidth)
+{
+    this.element = document.createElement("div");
+    this.element.className = "sidebar-overlay";
+
+    this._view = view;
+    this._widthSettingName = widthSettingName;
+    this._minimalWidth = minimalWidth;
+    this._savedWidth = minimalWidth || 300;
+    
+    if (this._widthSettingName)
+        WebInspector.settings[this._widthSettingName] = WebInspector.settings.createSetting(this._widthSettingName, undefined);
+    
+    this._resizerElement = document.createElement("div");
+    this._resizerElement.className = "sidebar-overlay-resizer";
+    this._installResizer(this._resizerElement);
+}
+
+WebInspector.SidebarOverlay.prototype = {
+    /**
+     * @param {Element} relativeToElement
+     */
+    show: function(relativeToElement)
+    {
+        relativeToElement.appendChild(this.element);
+        relativeToElement.addStyleClass("sidebar-overlay-shown");
+        this._view.show(this.element);
+        this.element.appendChild(this._resizerElement);
+        if (this._resizerWidgetElement)
+            this.element.appendChild(this._resizerWidgetElement);
+        this.position(relativeToElement);
+    },
+
+    /**
+     * @param {Element} relativeToElement
+     */
+    position: function(relativeToElement)
+    {
+        this._totalWidth = relativeToElement.offsetWidth;
+        this._setWidth(this._preferredWidth());
+    },
+
+    focus: function()
+    {
+        WebInspector.setCurrentFocusElement(this._view.element);
+    },
+
+    hide: function()
+    {
+        var element = this.element.parentElement;
+        if (!element)
+            return;
+
+        this._view.detach();
+        element.removeChild(this.element);
+        element.removeStyleClass("sidebar-overlay-shown");
+        this.element.removeChild(this._resizerElement);
+        if (this._resizerWidgetElement)
+            this.element.removeChild(this._resizerWidgetElement);
+    },
+    
+    /**
+     * @param {number} newWidth
+     */
+    _setWidth: function(newWidth)
+    {
+        var width = Number.constrain(newWidth, this._minimalWidth, this._totalWidth);
+        
+        if (this._width === width)
+            return;
+
+        this.element.style.width = width + "px";
+        this._resizerElement.style.left = (width - 3) + "px";
+        this._width = width;
+        this._view.doResize();
+        this._saveWidth();
+    },
+
+    /**
+     * @return {number}
+     */
+    _preferredWidth: function()
+    {
+        if (!this._widthSettingName)
+            return this._savedWidth;
+
+        return WebInspector.settings[this._widthSettingName].get() || this._savedWidth;
+    },
+    
+    _saveWidth: function()
+    {
+        this._savedWidth = this._width;
+        if (!this._widthSettingName)
+            return;
+
+        WebInspector.settings[this._widthSettingName].set(this._width);
+    },
+
+    /**
+     * @param {Event} event
+     * @return {boolean}
+     */
+    _startResizerDragging: function(event)
+    {
+        var width = this._width;
+        this._dragOffset = width - event.pageX;
+        return true;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _resizerDragging: function(event)
+    {
+        var width = event.pageX + this._dragOffset;
+        this._setWidth(width);
+        event.preventDefault();
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _endResizerDragging: function(event)
+    {
+        delete this._dragOffset;
+    },
+
+    /**
+     * @param {Element} resizerElement
+     */
+    _installResizer: function(resizerElement)
+    {
+        WebInspector.installDragHandle(resizerElement, this._startResizerDragging.bind(this), this._resizerDragging.bind(this), this._endResizerDragging.bind(this), "ew-resize");
+    },
+
+    /**
+     * @type {Element}
+     */
+    set resizerWidgetElement(resizerWidgetElement)
+    {
+        this._resizerWidgetElement = resizerWidgetElement;
+        this._installResizer(resizerWidgetElement);
+    }
+}
diff --git a/Source/devtools/front_end/SidebarPane.js b/Source/devtools/front_end/SidebarPane.js
new file mode 100644
index 0000000..f231184
--- /dev/null
+++ b/Source/devtools/front_end/SidebarPane.js
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2007 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.SidebarPane = function(title)
+{
+    WebInspector.View.call(this);
+    this.element.className = "sidebar-pane";
+
+    this.titleElement = document.createElement("div");
+    this.titleElement.className = "sidebar-pane-toolbar";
+
+    this.bodyElement = this.element.createChild("div", "body");
+
+    this._title = title;
+
+    this._expandCallback = null;
+}
+
+WebInspector.SidebarPane.EventTypes = {
+    wasShown: "wasShown"
+}
+
+WebInspector.SidebarPane.prototype = {
+    title: function()
+    {
+        return this._title;
+    },
+
+    /**
+     * @param {function()} callback
+     */
+    prepareContent: function(callback)
+    {
+        if (callback)
+            callback();
+    },
+
+    expand: function()
+    {
+        this.prepareContent(this.onContentReady.bind(this));
+    },
+
+    onContentReady: function()
+    {
+        if (this._expandCallback)
+            this._expandCallback();
+        else
+            this._expandPending = true;
+    },
+
+    /**
+     * @param {function()} callback
+     */
+    setExpandCallback: function(callback)
+    {
+        this._expandCallback = callback;
+        if (this._expandPending) {
+            delete this._expandPending;
+            this._expandCallback();
+        }
+    },
+
+    wasShown: function()
+    {
+        WebInspector.View.prototype.wasShown.call(this);
+        this.dispatchEventToListeners(WebInspector.SidebarPane.EventTypes.wasShown);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @param {Element} container
+ * @param {WebInspector.SidebarPane} pane
+ */
+WebInspector.SidebarPaneTitle = function(container, pane)
+{
+    this._pane = pane;
+
+    this.element = container.createChild("div", "sidebar-pane-title");
+    this.element.textContent = pane.title();
+    this.element.tabIndex = 0;
+    this.element.addEventListener("click", this._toggleExpanded.bind(this), false);
+    this.element.addEventListener("keydown", this._onTitleKeyDown.bind(this), false);
+    this.element.appendChild(this._pane.titleElement);
+
+    this._pane.setExpandCallback(this._expand.bind(this));
+}
+
+WebInspector.SidebarPaneTitle.prototype = {
+
+    _expand: function()
+    {
+        this.element.addStyleClass("expanded");
+        this._pane.show(this.element.parentNode, this.element.nextSibling);
+    },
+
+    _collapse: function()
+    {
+        this.element.removeStyleClass("expanded");
+        if (this._pane.element.parentNode == this.element.parentNode)
+            this._pane.detach();
+    },
+
+    _toggleExpanded: function()
+    {
+        if (this.element.hasStyleClass("expanded"))
+            this._collapse();
+        else
+            this._pane.expand();
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onTitleKeyDown: function(event)
+    {
+        if (isEnterKey(event) || event.keyCode === WebInspector.KeyboardShortcut.Keys.Space.code)
+            this._toggleExpanded();
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.SidebarPaneStack = function()
+{
+    WebInspector.View.call(this);
+    this.element.className = "sidebar-pane-stack fill";
+    this.registerRequiredCSS("sidebarPane.css");
+}
+
+WebInspector.SidebarPaneStack.prototype = {
+    /**
+     * @param {WebInspector.SidebarPane} pane
+     */
+    addPane: function(pane)
+    {
+        new WebInspector.SidebarPaneTitle(this.element, pane);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TabbedPane}
+ */
+WebInspector.SidebarTabbedPane = function()
+{
+    WebInspector.TabbedPane.call(this);
+    this.element.addStyleClass("sidebar-tabbed-pane");
+    this.registerRequiredCSS("sidebarPane.css");
+}
+
+WebInspector.SidebarTabbedPane.prototype = {
+    /**
+     * @param {WebInspector.SidebarPane} pane
+     */
+    addPane: function(pane)
+    {
+        var title = pane.title();
+        this.appendTab(title, title, pane);
+        pane.element.appendChild(pane.titleElement);
+        pane.setExpandCallback(this.selectTab.bind(this, title));
+
+    },
+
+    __proto__: WebInspector.TabbedPane.prototype
+}
diff --git a/Source/devtools/front_end/SidebarTreeElement.js b/Source/devtools/front_end/SidebarTreeElement.js
new file mode 100644
index 0000000..398abc8
--- /dev/null
+++ b/Source/devtools/front_end/SidebarTreeElement.js
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ */
+WebInspector.SidebarSectionTreeElement = function(title, representedObject, hasChildren)
+{
+    TreeElement.call(this, title.escapeHTML(), representedObject || {}, hasChildren);
+    this.expand();
+}
+
+WebInspector.SidebarSectionTreeElement.prototype = {
+    selectable: false,
+
+    collapse: function()
+    {
+        // Should not collapse as it is not selectable.
+    },
+
+    get smallChildren()
+    {
+        return this._smallChildren;
+    },
+
+    set smallChildren(x)
+    {
+        if (this._smallChildren === x)
+            return;
+
+        this._smallChildren = x;
+
+        if (this._smallChildren)
+            this._childrenListNode.addStyleClass("small");
+        else
+            this._childrenListNode.removeStyleClass("small");
+    },
+
+    onattach: function()
+    {
+        this._listItemNode.addStyleClass("sidebar-tree-section");
+    },
+
+    onreveal: function()
+    {
+        if (this.listItemElement)
+            this.listItemElement.scrollIntoViewIfNeeded(false);
+    },
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {string=} subtitle
+ * @param {Object=} representedObject
+ * @param {boolean=} hasChildren
+ */
+WebInspector.SidebarTreeElement = function(className, title, subtitle, representedObject, hasChildren)
+{
+    TreeElement.call(this, "", representedObject, hasChildren);
+
+    if (hasChildren) {
+        this.disclosureButton = document.createElement("button");
+        this.disclosureButton.className = "disclosure-button";
+    }
+
+    if (!this.iconElement) {
+        this.iconElement = document.createElement("img");
+        this.iconElement.className = "icon";
+    }
+
+    this.statusElement = document.createElement("div");
+    this.statusElement.className = "status";
+
+    this.titlesElement = document.createElement("div");
+    this.titlesElement.className = "titles";
+
+    this.titleElement = document.createElement("span");
+    this.titleElement.className = "title";
+    this.titlesElement.appendChild(this.titleElement);
+
+    this.subtitleElement = document.createElement("span");
+    this.subtitleElement.className = "subtitle";
+    this.titlesElement.appendChild(this.subtitleElement);
+
+    this.className = className;
+    this.mainTitle = title;
+    this.subtitle = subtitle;
+}
+
+WebInspector.SidebarTreeElement.prototype = {
+    get small()
+    {
+        return this._small;
+    },
+
+    set small(x)
+    {
+        this._small = x;
+
+        if (this._listItemNode) {
+            if (this._small)
+                this._listItemNode.addStyleClass("small");
+            else
+                this._listItemNode.removeStyleClass("small");
+        }
+    },
+
+    get mainTitle()
+    {
+        return this._mainTitle;
+    },
+
+    set mainTitle(x)
+    {
+        this._mainTitle = x;
+        this.refreshTitles();
+    },
+
+    get subtitle()
+    {
+        return this._subtitle;
+    },
+
+    set subtitle(x)
+    {
+        this._subtitle = x;
+        this.refreshTitles();
+    },
+
+    get bubbleText()
+    {
+        return this._bubbleText;
+    },
+
+    set bubbleText(x)
+    {
+        if (!this.bubbleElement) {
+            this.bubbleElement = document.createElement("div");
+            this.bubbleElement.className = "bubble";
+            this.statusElement.appendChild(this.bubbleElement);
+        }
+
+        this._bubbleText = x;
+        this.bubbleElement.textContent = x;
+    },
+
+    set wait(x)
+    {
+        if (x)
+            this._listItemNode.addStyleClass("wait");
+        else
+            this._listItemNode.removeStyleClass("wait");
+    },
+
+    refreshTitles: function()
+    {
+        var mainTitle = this.mainTitle;
+        if (this.titleElement.textContent !== mainTitle)
+            this.titleElement.textContent = mainTitle;
+
+        var subtitle = this.subtitle;
+        if (subtitle) {
+            if (this.subtitleElement.textContent !== subtitle)
+                this.subtitleElement.textContent = subtitle;
+            this.titlesElement.removeStyleClass("no-subtitle");
+        } else {
+            this.subtitleElement.textContent = "";
+            this.titlesElement.addStyleClass("no-subtitle");
+        }
+    },
+
+    isEventWithinDisclosureTriangle: function(event)
+    {
+        return event.target === this.disclosureButton;
+    },
+
+    onattach: function()
+    {
+        this._listItemNode.addStyleClass("sidebar-tree-item");
+
+        if (this.className)
+            this._listItemNode.addStyleClass(this.className);
+
+        if (this.small)
+            this._listItemNode.addStyleClass("small");
+
+        if (this.hasChildren && this.disclosureButton)
+            this._listItemNode.appendChild(this.disclosureButton);
+
+        this._listItemNode.appendChild(this.iconElement);
+        this._listItemNode.appendChild(this.statusElement);
+        this._listItemNode.appendChild(this.titlesElement);
+    },
+
+    onreveal: function()
+    {
+        if (this._listItemNode)
+            this._listItemNode.scrollIntoViewIfNeeded(false);
+    },
+
+    __proto__: TreeElement.prototype
+}
diff --git a/Source/devtools/front_end/SidebarView.js b/Source/devtools/front_end/SidebarView.js
new file mode 100644
index 0000000..fa84e88
--- /dev/null
+++ b/Source/devtools/front_end/SidebarView.js
@@ -0,0 +1,164 @@
+/*
+ * 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SplitView}
+ * @param {string=} sidebarPosition
+ * @param {string=} sidebarWidthSettingName
+ * @param {number=} defaultSidebarWidth
+ * @param {number=} defaultSidebarHeight
+ */
+WebInspector.SidebarView = function(sidebarPosition, sidebarWidthSettingName, defaultSidebarWidth, defaultSidebarHeight)
+{
+    WebInspector.SplitView.call(this, true, sidebarWidthSettingName, defaultSidebarWidth, defaultSidebarHeight);
+
+    this._minimumSidebarWidth = Preferences.minSidebarWidth;
+    this._minimumMainWidthPercent = 50;
+
+    this._minimumSidebarHeight = Preferences.minSidebarHeight;
+    this._minimumMainHeightPercent = 50;
+
+    this._sidebarPosition = sidebarPosition || WebInspector.SidebarView.SidebarPosition.Start;
+    this.setSecondIsSidebar(this._sidebarPosition === WebInspector.SidebarView.SidebarPosition.End);
+}
+
+WebInspector.SidebarView.EventTypes = {
+    Resized: "Resized"
+}
+
+/**
+ * @enum {string}
+ */
+WebInspector.SidebarView.SidebarPosition = {
+    Start: "Start",
+    End: "End"
+}
+
+WebInspector.SidebarView.prototype = {
+    /**
+     * @param {number} width
+     */
+    setMinimumSidebarWidth: function(width)
+    {
+        this._minimumSidebarWidth = width;
+    },
+
+    /**
+     * @param {number} height
+     */
+    setMinimumSidebarHeight: function(height)
+    {
+        this._minimumSidebarHeight = height;
+    },
+
+    /**
+     * @param {number} widthPercent
+     */
+    setMinimumMainWidthPercent: function(widthPercent)
+    {
+        this._minimumMainWidthPercent = widthPercent;
+    },
+
+    /**
+     * @param {number} heightPercent
+     */
+    setMinimumMainHeightPercent: function(heightPercent)
+    {
+        this._minimumMainHeightPercent = heightPercent;
+    },
+
+    /**
+     * @param {number} width
+     */
+    setSidebarWidth: function(width)
+    {
+        this.setSidebarSize(width);
+    },
+
+    /**
+     * @return {number}
+     */
+    sidebarWidth: function()
+    {
+        return this.sidebarSize();
+    },
+
+    onResize: function()
+    {
+        WebInspector.SplitView.prototype.onResize.call(this);
+        this.dispatchEventToListeners(WebInspector.SidebarView.EventTypes.Resized, this.sidebarWidth());
+    },
+
+    /**
+     * @param {number} size
+     */
+    applyConstraints: function(size)
+    {
+        var from = this.isVertical() ? this._minimumSidebarWidth : this._minimumSidebarHeight;
+        var minMainSizePercent = this.isVertical() ? this._minimumMainWidthPercent : this._minimumMainHeightPercent;
+        var to = this.totalSize() * (100 - minMainSizePercent) / 100;
+        return from > to ? -1 : Number.constrain(size, from, to);
+    },
+
+    hideMainElement: function()
+    {
+        if (this.isSidebarSecond())
+            this.showOnlySecond();
+        else
+            this.showOnlyFirst();
+    },
+
+    showMainElement: function()
+    {
+        this.showBoth();
+    },
+
+    hideSidebarElement: function()
+    {
+        if (this.isSidebarSecond())
+            this.showOnlyFirst();
+        else
+            this.showOnlySecond();
+    },
+
+    showSidebarElement: function()
+    {
+        this.showBoth();
+    },
+
+    /**
+     * @return {Array.<Element>}
+     */
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return [ this.mainElement, this.sidebarElement ];
+    },
+
+    __proto__: WebInspector.SplitView.prototype
+}
diff --git a/Source/devtools/front_end/SimpleWorkspaceProvider.js b/Source/devtools/front_end/SimpleWorkspaceProvider.js
new file mode 100644
index 0000000..4e54991
--- /dev/null
+++ b/Source/devtools/front_end/SimpleWorkspaceProvider.js
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.ContentProviderBasedProjectDelegate}
+ * @param {string} name
+ * @param {string} type
+ */
+WebInspector.SimpleProjectDelegate = function(name, type)
+{
+    WebInspector.ContentProviderBasedProjectDelegate.call(this, type);
+    this._name = name;
+    this._lastUniqueSuffix = 0;
+}
+
+WebInspector.SimpleProjectDelegate.projectId = function(name, type)
+{
+    var typePrefix = type !== WebInspector.projectTypes.Network ? (type + ":") : "";
+    return typePrefix + name;
+}
+
+WebInspector.SimpleProjectDelegate.prototype = {
+    /**
+     * @return {string}
+     */
+    id: function()
+    {
+        return WebInspector.SimpleProjectDelegate.projectId(this._name, this.type());
+    },
+
+    /**
+     * @return {string}
+     */
+    displayName: function()
+    {
+        if (typeof this._displayName !== "undefined")
+            return this._displayName;
+        if (!this._name) {
+            this._displayName = this.type() !== WebInspector.projectTypes.Snippets ? WebInspector.UIString("(no domain)") : "";
+            return this._displayName;
+        }
+        var parsedURL = new WebInspector.ParsedURL(this._name);
+        if (parsedURL.isValid) {
+            this._displayName = parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "");
+            if (!this._displayName)
+                this._displayName = this._name;
+        }
+        else
+            this._displayName = this._name;
+        return this._displayName;
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {string} url
+     * @param {WebInspector.ContentProvider} contentProvider
+     * @param {boolean} isEditable
+     * @param {boolean=} isContentScript
+     * @return {Array.<string>}
+     */
+    addFile: function(path, forceUniquePath, url, contentProvider, isEditable, isContentScript)
+    {
+        if (forceUniquePath)
+            this._ensureUniquePath(path);
+        return this.addContentProvider(path, url, contentProvider, isEditable, isContentScript);
+    },
+
+    /**
+     * @param {Array.<string>} path
+     */
+    _ensureUniquePath: function(path)
+     {
+        var uniquePath = path.join("/");
+        var suffix = "";
+        var contentProviders = this.contentProviders();
+        while (contentProviders[uniquePath]) {
+            suffix = " (" + (++this._lastUniqueSuffix) + ")";
+            uniquePath = path + suffix;
+        }
+        path[path.length - 1] += suffix;
+    },
+    
+    __proto__: WebInspector.ContentProviderBasedProjectDelegate.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.Workspace} workspace
+ * @param {string} type
+ */
+WebInspector.SimpleWorkspaceProvider = function(workspace, type)
+{
+    this._workspace = workspace;
+    this._type = type;
+    this._simpleProjectDelegates = {};
+}
+
+/**
+ * @param {Array.<string>} splittedURL
+ * @return {Array.<string>}
+ */
+WebInspector.SimpleWorkspaceProvider.pathForSplittedURL = function(splittedURL)
+{
+    var result = splittedURL.slice();
+    result.shift();
+    return result;
+}
+
+WebInspector.SimpleWorkspaceProvider.prototype = {
+    /**
+     * @param {string} projectName
+     * @return {WebInspector.ProjectDelegate}
+     */
+    _projectDelegate: function(projectName)
+    {
+        if (this._simpleProjectDelegates[projectName])
+            return this._simpleProjectDelegates[projectName];
+        var simpleProjectDelegate = new WebInspector.SimpleProjectDelegate(projectName, this._type);
+        this._simpleProjectDelegates[projectName] = simpleProjectDelegate;
+        this._workspace.addProject(simpleProjectDelegate);
+        return simpleProjectDelegate;
+    },
+ 
+    /**
+     * @param {string} url
+     * @param {WebInspector.ContentProvider} contentProvider
+     * @param {boolean} isEditable
+     * @param {boolean=} isContentScript
+     * @return {WebInspector.UISourceCode}
+     */
+    addFileForURL: function(url, contentProvider, isEditable, isContentScript)
+    {
+        return this._innerAddFileForURL(url, contentProvider, isEditable, false, isContentScript);
+    },
+
+    /**
+     * @param {string} url
+     * @param {WebInspector.ContentProvider} contentProvider
+     * @param {boolean} isEditable
+     * @param {boolean=} isContentScript
+     * @return {WebInspector.UISourceCode}
+     */
+    addUniqueFileForURL: function(url, contentProvider, isEditable, isContentScript)
+    {
+        return this._innerAddFileForURL(url, contentProvider, isEditable, true, isContentScript);
+    },
+
+    /**
+     * @param {string} url
+     * @param {WebInspector.ContentProvider} contentProvider
+     * @param {boolean} isEditable
+     * @param {boolean} forceUnique
+     * @param {boolean=} isContentScript
+     * @return {WebInspector.UISourceCode}
+     */
+    _innerAddFileForURL: function(url, contentProvider, isEditable, forceUnique, isContentScript)
+    {
+        var splittedURL = WebInspector.ParsedURL.splitURL(url);
+        var projectName = splittedURL[0];
+        var path = WebInspector.SimpleWorkspaceProvider.pathForSplittedURL(splittedURL);
+        return this._innerAddFile(projectName, path, url, contentProvider, isEditable, forceUnique, isContentScript);
+    },
+
+    /**
+     * @param {string} projectName
+     * @param {string} name
+     * @param {WebInspector.ContentProvider} contentProvider
+     * @param {boolean} isEditable
+     * @param {boolean=} isContentScript
+     * @return {WebInspector.UISourceCode}
+     */
+    addFileByName: function(projectName, name, contentProvider, isEditable, isContentScript)
+    {
+        return this._innerAddFile("", [name], name, contentProvider, isEditable, false, isContentScript);
+    },
+
+    /**
+     * @param {string} projectName
+     * @param {Array.<string>} path
+     * @param {WebInspector.ContentProvider} contentProvider
+     * @param {boolean} isEditable
+     * @param {boolean} forceUnique
+     * @param {boolean=} isContentScript
+     * @return {WebInspector.UISourceCode}
+     */
+    _innerAddFile: function(projectName, path, url, contentProvider, isEditable, forceUnique, isContentScript)
+    {
+        var projectDelegate = this._projectDelegate(projectName);
+        path = projectDelegate.addFile(path, forceUnique, url, contentProvider, isEditable, isContentScript);
+        return this._workspace.uiSourceCode(projectDelegate.id(), path);
+    },
+
+    /**
+     * @param {string} projectName
+     * @param {string} name
+     */
+    removeFileByName: function(projectName, name)
+    {
+        var projectDelegate = this._projectDelegate(projectName);
+        projectDelegate.removeFile([name]);
+    },
+
+    reset: function()
+    {
+        for (var projectName in this._simpleProjectDelegates)
+            this._simpleProjectDelegates[projectName].reset();
+        this._simpleProjectDelegates = {};
+    },
+    
+    __proto__: WebInspector.Object.prototype
+}
diff --git a/Source/devtools/front_end/SnippetStorage.js b/Source/devtools/front_end/SnippetStorage.js
new file mode 100644
index 0000000..4bdc873
--- /dev/null
+++ b/Source/devtools/front_end/SnippetStorage.js
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.SnippetStorage = function(settingPrefix, namePrefix)
+{
+    this._snippets = {};
+
+    this._lastSnippetIdentifierSetting = WebInspector.settings.createSetting(settingPrefix + "Snippets_lastIdentifier", 0);
+    this._snippetsSetting = WebInspector.settings.createSetting(settingPrefix + "Snippets", []);
+    this._namePrefix = namePrefix;
+
+    this._loadSettings();
+}
+
+WebInspector.SnippetStorage.prototype = {
+    get namePrefix()
+    {
+        return this._namePrefix;
+    },
+
+    _saveSettings: function()
+    {
+        var savedSnippets = [];
+        for (var id in this._snippets)
+            savedSnippets.push(this._snippets[id].serializeToObject());
+        this._snippetsSetting.set(savedSnippets);
+    },
+
+    /**
+     * @return {Array.<WebInspector.Snippet>}
+     */
+    snippets: function()
+    {
+        var result = [];
+        for (var id in this._snippets)
+            result.push(this._snippets[id]);
+        return result;
+    },
+
+    /**
+     * @param {string} id
+     * @return {WebInspector.Snippet}
+     */
+    snippetForId: function(id)
+    {
+        return this._snippets[id];
+    },
+
+    _loadSettings: function()
+    {
+        var savedSnippets = this._snippetsSetting.get();
+        for (var i = 0; i < savedSnippets.length; ++i)
+            this._snippetAdded(WebInspector.Snippet.fromObject(this, savedSnippets[i]));
+    },
+
+    /**
+     * @param {WebInspector.Snippet} snippet
+     */
+    deleteSnippet: function(snippet)
+    {
+        delete this._snippets[snippet.id];
+        this._saveSettings();
+    },
+
+    /**
+     * @return {WebInspector.Snippet}
+     */
+    createSnippet: function()
+    {
+        var nextId = this._lastSnippetIdentifierSetting.get() + 1;
+        var snippetId = String(nextId);
+        this._lastSnippetIdentifierSetting.set(nextId);
+        var snippet = new WebInspector.Snippet(this, snippetId);
+        this._snippetAdded(snippet);
+        this._saveSettings();
+        return snippet;
+    },
+
+    /**
+     * @param {WebInspector.Snippet} snippet
+     */
+    _snippetAdded: function(snippet)
+    {
+        this._snippets[snippet.id] = snippet;
+    },
+    
+    reset: function()
+    {
+        this._lastSnippetIdentifierSetting.set(0);
+        this._snippetsSetting.set([]);
+        this._snippets = {};
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.SnippetStorage} storage
+ * @param {string} id
+ * @param {string=} name
+ * @param {string=} content
+ */
+WebInspector.Snippet = function(storage, id, name, content)
+{
+    this._storage = storage;
+    this._id = id;
+    this._name = name || storage.namePrefix + id;
+    this._content = content || "";
+}
+
+/**
+ * @param {WebInspector.SnippetStorage} storage
+ * @param {Object} serializedSnippet
+ * @return {WebInspector.Snippet}
+ */
+WebInspector.Snippet.fromObject = function(storage, serializedSnippet)
+{
+    return new WebInspector.Snippet(storage, serializedSnippet.id, serializedSnippet.name, serializedSnippet.content);
+}
+
+WebInspector.Snippet.prototype = {
+    /**
+     * @return {string}
+     */
+    get id()
+    {
+        return this._id;
+    },
+
+    /**
+     * @return {string}
+     */
+    get name()
+    {
+        return this._name;
+    },
+
+    set name(name)
+    {
+        if (this._name === name)
+            return;
+
+        this._name = name;
+        this._storage._saveSettings();
+    },
+
+    /**
+     * @return {string}
+     */
+    get content()
+    {
+        return this._content;
+    },
+
+    set content(content)
+    {
+        if (this._content === content)
+            return;
+
+        this._content = content;
+        this._storage._saveSettings();
+    },
+
+    /**
+     * @return {Object}
+     */
+    serializeToObject: function()
+    {
+        var serializedSnippet = {};
+        serializedSnippet.id = this.id;
+        serializedSnippet.name = this.name;
+        serializedSnippet.content = this.content;
+        return serializedSnippet;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
diff --git a/Source/devtools/front_end/SoftContextMenu.js b/Source/devtools/front_end/SoftContextMenu.js
new file mode 100644
index 0000000..af2ed1e
--- /dev/null
+++ b/Source/devtools/front_end/SoftContextMenu.js
@@ -0,0 +1,400 @@
+/*
+ * 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.SoftContextMenu=} parentMenu
+ */
+WebInspector.SoftContextMenu = function(items, parentMenu)
+{
+    this._items = items;
+    this._parentMenu = parentMenu;
+}
+
+WebInspector.SoftContextMenu.prototype = {
+    /**
+     * @param {boolean=} alignToCurrentTarget
+     */
+    show: function(event, alignToCurrentTarget)
+    {
+        this._x = event.x;
+        this._y = event.y;
+        this._time = new Date().getTime();
+
+        // Absolutely position menu for iframes.
+        var absoluteX = event.pageX;
+        var absoluteY = event.pageY;
+        var targetElement = event.target;
+        while (targetElement && window !== targetElement.ownerDocument.defaultView) {
+            var frameElement = targetElement.ownerDocument.defaultView.frameElement;
+            absoluteY += frameElement.totalOffsetTop();
+            absoluteX += frameElement.totalOffsetLeft();
+            targetElement = frameElement;
+        }
+
+        // Create context menu.
+        var targetRect;
+        this._contextMenuElement = document.createElement("div");
+        this._contextMenuElement.className = "soft-context-menu";
+        this._contextMenuElement.tabIndex = 0;
+        if (alignToCurrentTarget) {
+            targetRect = event.currentTarget.getBoundingClientRect();
+            // Align with bottom left of currentTarget by default.
+            absoluteX = targetRect.left;
+            absoluteY = targetRect.bottom;
+        }
+        this._contextMenuElement.style.top = absoluteY + "px";
+        this._contextMenuElement.style.left = absoluteX + "px";
+
+        this._contextMenuElement.addEventListener("mouseup", consumeEvent, false);
+        this._contextMenuElement.addEventListener("keydown", this._menuKeyDown.bind(this), false);
+
+        for (var i = 0; i < this._items.length; ++i)
+            this._contextMenuElement.appendChild(this._createMenuItem(this._items[i]));
+
+        // Install glass pane capturing events.
+        if (!this._parentMenu) {
+            this._glassPaneElement = document.createElement("div");
+            this._glassPaneElement.className = "soft-context-menu-glass-pane";
+            this._glassPaneElement.tabIndex = 0;
+            this._glassPaneElement.addEventListener("mouseup", this._glassPaneMouseUp.bind(this), false);
+            this._glassPaneElement.appendChild(this._contextMenuElement);
+            document.body.appendChild(this._glassPaneElement);
+            this._focus();
+        } else
+            this._parentMenu._parentGlassPaneElement().appendChild(this._contextMenuElement);
+
+        // Re-position menu in case it does not fit.
+        if (document.body.offsetWidth <  this._contextMenuElement.offsetLeft + this._contextMenuElement.offsetWidth) {
+            if (alignToCurrentTarget)
+                this._contextMenuElement.style.left = Math.max(0, targetRect.right - this._contextMenuElement.offsetWidth) + "px";
+            else
+                this._contextMenuElement.style.left = (absoluteX - this._contextMenuElement.offsetWidth) + "px";
+        }
+        if (document.body.offsetHeight < this._contextMenuElement.offsetTop + this._contextMenuElement.offsetHeight) {
+            if (alignToCurrentTarget)
+                this._contextMenuElement.style.top = Math.max(0, targetRect.top - this._contextMenuElement.offsetHeight) + "px";
+            else
+                this._contextMenuElement.style.top = (document.body.offsetHeight - this._contextMenuElement.offsetHeight) + "px";
+        }
+
+        event.consume(true);
+    },
+
+    _parentGlassPaneElement: function()
+    {
+        if (this._glassPaneElement)
+            return this._glassPaneElement;
+        if (this._parentMenu)
+            return this._parentMenu._parentGlassPaneElement();
+        return null;
+    },
+
+    _createMenuItem: function(item)
+    {
+        if (item.type === "separator")
+            return this._createSeparator();
+
+        if (item.type === "subMenu")
+            return this._createSubMenu(item);
+
+        var menuItemElement = document.createElement("div");
+        menuItemElement.className = "soft-context-menu-item";
+
+        var checkMarkElement = document.createElement("span");
+        checkMarkElement.textContent = "\u2713 "; // Checkmark Unicode symbol
+        checkMarkElement.className = "soft-context-menu-item-checkmark";
+        if (!item.checked)
+            checkMarkElement.style.opacity = "0";
+
+        menuItemElement.appendChild(checkMarkElement);
+        menuItemElement.appendChild(document.createTextNode(item.label));
+
+        menuItemElement.addEventListener("mousedown", this._menuItemMouseDown.bind(this), false);
+        menuItemElement.addEventListener("mouseup", this._menuItemMouseUp.bind(this), false);
+
+        // Manually manage hover highlight since :hover does not work in case of click-and-hold menu invocation.
+        menuItemElement.addEventListener("mouseover", this._menuItemMouseOver.bind(this), false);
+        menuItemElement.addEventListener("mouseout", this._menuItemMouseOut.bind(this), false);
+
+        menuItemElement._actionId = item.id;
+        return menuItemElement;
+    },
+
+    _createSubMenu: function(item)
+    {
+        var menuItemElement = document.createElement("div");
+        menuItemElement.className = "soft-context-menu-item";
+        menuItemElement._subItems = item.subItems;
+
+        // Occupy the same space on the left in all items.
+        var checkMarkElement = document.createElement("span");
+        checkMarkElement.textContent = "\u2713 "; // Checkmark Unicode symbol
+        checkMarkElement.className = "soft-context-menu-item-checkmark";
+        checkMarkElement.style.opacity = "0";
+        menuItemElement.appendChild(checkMarkElement);
+
+        var subMenuArrowElement = document.createElement("span");
+        subMenuArrowElement.textContent = "\u25B6"; // BLACK RIGHT-POINTING TRIANGLE
+        subMenuArrowElement.className = "soft-context-menu-item-submenu-arrow";
+
+        menuItemElement.appendChild(document.createTextNode(item.label));
+        menuItemElement.appendChild(subMenuArrowElement);
+
+        menuItemElement.addEventListener("mousedown", this._menuItemMouseDown.bind(this), false);
+        menuItemElement.addEventListener("mouseup", this._menuItemMouseUp.bind(this), false);
+
+        // Manually manage hover highlight since :hover does not work in case of click-and-hold menu invocation.
+        menuItemElement.addEventListener("mouseover", this._menuItemMouseOver.bind(this), false);
+        menuItemElement.addEventListener("mouseout", this._menuItemMouseOut.bind(this), false);
+
+        return menuItemElement;
+    },
+
+    _createSeparator: function()
+    {
+        var separatorElement = document.createElement("div");
+        separatorElement.className = "soft-context-menu-separator";
+        separatorElement._isSeparator = true;
+        separatorElement.addEventListener("mouseover", this._hideSubMenu.bind(this), false);
+        separatorElement.createChild("div", "separator-line");
+        return separatorElement;
+    },
+
+    _menuItemMouseDown: function(event)
+    {
+        // Do not let separator's mouse down hit menu's handler - we need to receive mouse up!
+        event.consume(true);
+    },
+
+    _menuItemMouseUp: function(event)
+    {
+        this._triggerAction(event.target, event);
+        event.consume();
+    },
+
+    _focus: function()
+    {
+        this._contextMenuElement.focus();
+    },
+
+    _triggerAction: function(menuItemElement, event)
+    {
+        if (!menuItemElement._subItems) {
+            this._discardMenu(true, event);
+            if (typeof menuItemElement._actionId !== "undefined") {
+                WebInspector.contextMenuItemSelected(menuItemElement._actionId);
+                delete menuItemElement._actionId;
+            }
+            return;
+        }
+
+        this._showSubMenu(menuItemElement, event);
+        event.consume();
+    },
+
+    _showSubMenu: function(menuItemElement, event)
+    {
+        if (menuItemElement._subMenuTimer) {
+            clearTimeout(menuItemElement._subMenuTimer);
+            delete menuItemElement._subMenuTimer;
+        }
+        if (this._subMenu)
+            return;
+
+        this._subMenu = new WebInspector.SoftContextMenu(menuItemElement._subItems, this);
+        this._subMenu.show(this._buildMouseEventForSubMenu(menuItemElement));
+    },
+
+    _buildMouseEventForSubMenu: function(subMenuItemElement)
+    {
+        var subMenuOffset = { x: subMenuItemElement.offsetWidth - 3, y: subMenuItemElement.offsetTop - 1 };
+        var targetX = this._x + subMenuOffset.x;
+        var targetY = this._y + subMenuOffset.y;
+        var targetPageX = parseInt(this._contextMenuElement.style.left, 10) + subMenuOffset.x;
+        var targetPageY = parseInt(this._contextMenuElement.style.top, 10) + subMenuOffset.y;
+        return { x: targetX, y: targetY, pageX: targetPageX, pageY: targetPageY, consume: function() {} };
+    },
+
+    _hideSubMenu: function()
+    {
+        if (!this._subMenu)
+            return;
+        this._subMenu._discardSubMenus();
+        this._focus();
+    },
+
+    _menuItemMouseOver: function(event)
+    {
+        this._highlightMenuItem(event.target);
+    },
+
+    _menuItemMouseOut: function(event)
+    {
+        if (!this._subMenu || !event.relatedTarget) {
+            this._highlightMenuItem(null);
+            return;
+        }
+
+        var relatedTarget = event.relatedTarget;
+        if (this._contextMenuElement.isSelfOrAncestor(relatedTarget) || relatedTarget.hasStyleClass("soft-context-menu-glass-pane"))
+            this._highlightMenuItem(null);
+    },
+
+    _highlightMenuItem: function(menuItemElement)
+    {
+        if (this._highlightedMenuItemElement ===  menuItemElement)
+            return;
+
+        this._hideSubMenu();
+        if (this._highlightedMenuItemElement) {
+            this._highlightedMenuItemElement.removeStyleClass("soft-context-menu-item-mouse-over");
+            if (this._highlightedMenuItemElement._subItems && this._highlightedMenuItemElement._subMenuTimer) {
+                clearTimeout(this._highlightedMenuItemElement._subMenuTimer);
+                delete this._highlightedMenuItemElement._subMenuTimer;
+            }
+        }
+        this._highlightedMenuItemElement = menuItemElement;
+        if (this._highlightedMenuItemElement) {
+            this._highlightedMenuItemElement.addStyleClass("soft-context-menu-item-mouse-over");
+            this._contextMenuElement.focus();
+            if (this._highlightedMenuItemElement._subItems && !this._highlightedMenuItemElement._subMenuTimer)
+                this._highlightedMenuItemElement._subMenuTimer = setTimeout(this._showSubMenu.bind(this, this._highlightedMenuItemElement, this._buildMouseEventForSubMenu(this._highlightedMenuItemElement)), 150);
+        }
+    },
+
+    _highlightPrevious: function()
+    {
+        var menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.previousSibling : this._contextMenuElement.lastChild;
+        while (menuItemElement && menuItemElement._isSeparator)
+            menuItemElement = menuItemElement.previousSibling;
+        if (menuItemElement)
+            this._highlightMenuItem(menuItemElement);
+    },
+
+    _highlightNext: function()
+    {
+        var menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.nextSibling : this._contextMenuElement.firstChild;
+        while (menuItemElement && menuItemElement._isSeparator)
+            menuItemElement = menuItemElement.nextSibling;
+        if (menuItemElement)
+            this._highlightMenuItem(menuItemElement);
+    },
+
+    _menuKeyDown: function(event)
+    {
+        switch (event.keyIdentifier) {
+        case "Up":
+            this._highlightPrevious(); break;
+        case "Down":
+            this._highlightNext(); break;
+        case "Left":
+            if (this._parentMenu) {
+                this._highlightMenuItem(null);
+                this._parentMenu._focus();
+            }
+            break;
+        case "Right":
+            if (!this._highlightedMenuItemElement)
+                break;
+            if (this._highlightedMenuItemElement._subItems) {
+                this._showSubMenu(this._highlightedMenuItemElement, this._buildMouseEventForSubMenu(this._highlightedMenuItemElement));
+                this._subMenu._focus();
+                this._subMenu._highlightNext();
+            }
+            break;
+        case "U+001B": // Escape
+            this._discardMenu(true, event); break;
+        case "Enter":
+            if (!isEnterKey(event))
+                break;
+            // Fall through
+        case "U+0020": // Space
+            if (this._highlightedMenuItemElement)
+                this._triggerAction(this._highlightedMenuItemElement, event);
+            break;
+        }
+        event.consume(true);
+    },
+
+    _glassPaneMouseUp: function(event)
+    {
+        // Return if this is simple 'click', since dispatched on glass pane, can't use 'click' event.
+        if (event.x === this._x && event.y === this._y && new Date().getTime() - this._time < 300)
+            return;
+        this._discardMenu(true, event);
+        event.consume();
+    },
+
+    /**
+     * @param {boolean} closeParentMenus
+     * @param {Event=} event
+     */
+    _discardMenu: function(closeParentMenus, event)
+    {
+        if (this._subMenu && !closeParentMenus)
+            return;
+        if (this._glassPaneElement) {
+            var glassPane = this._glassPaneElement;
+            delete this._glassPaneElement;
+            // This can re-enter discardMenu due to blur.
+            document.body.removeChild(glassPane);
+            if (this._parentMenu) {
+                delete this._parentMenu._subMenu;
+                if (closeParentMenus)
+                    this._parentMenu._discardMenu(closeParentMenus, event);
+            }
+
+            if (event)
+                event.consume(true);
+        } else if (this._parentMenu && this._contextMenuElement.parentElement) {
+            this._discardSubMenus();
+            if (closeParentMenus)
+                this._parentMenu._discardMenu(closeParentMenus, event);
+
+            if (event)
+                event.consume(true);
+        }
+    },
+
+    _discardSubMenus: function()
+    {
+        if (this._subMenu)
+            this._subMenu._discardSubMenus();
+        if (this._contextMenuElement.parentElement)
+            this._contextMenuElement.parentElement.removeChild(this._contextMenuElement);
+        if (this._parentMenu)
+            delete this._parentMenu._subMenu;
+    }
+}
+
+if (!InspectorFrontendHost.showContextMenu) {
+
+InspectorFrontendHost.showContextMenu = function(event, items)
+{
+    new WebInspector.SoftContextMenu(items).show(event);
+}
+
+}
diff --git a/Source/devtools/front_end/SourceCSSTokenizer.js b/Source/devtools/front_end/SourceCSSTokenizer.js
new file mode 100644
index 0000000..4798ae0
--- /dev/null
+++ b/Source/devtools/front_end/SourceCSSTokenizer.js
@@ -0,0 +1,1571 @@
+/* Generated by re2c 0.13.5 on Fri Sep  7 17:09:30 2012 */
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+// Generate js file as follows:
+
+/*
+re2c -isc devtools/front_end/SourceCSSTokenizer.re2js \
+  | sed 's|^yy\([^:]*\)*\:|case \1:|' \
+  | sed 's|[*]cursor[+][+]|this._charAt(cursor++)|' \
+  | sed 's|[[*][+][+]cursor|this._charAt(++cursor)|' \
+  | sed 's|[*]cursor|this._charAt(cursor)|' \
+  | sed 's|yych = \*\([^;]*\)|yych = this._charAt\1|' \
+  | sed 's|{ gotoCase = \([^; continue; };]*\)|{ gotoCase = \1; continue; }|' \
+  | sed 's|yych <= \(0x[0-9a-fA-F]*\)|yych \<\= String.fromCharCode(\1)|' \
+  | sed 's|unsigned\ int|var|' \
+  | sed 's|var\ yych|case 1: case 1: var yych|' > devtools/front_end/SourceCSSTokenizer.js
+*/
+
+/**
+ * @constructor
+ * @extends {WebInspector.SourceTokenizer}
+ */
+WebInspector.SourceCSSTokenizer = function()
+{
+    WebInspector.SourceTokenizer.call(this);
+
+    this._propertyKeywords = WebInspector.CSSMetadata.cssPropertiesMetainfoKeySet();
+    this._colorKeywords = WebInspector.CSSMetadata.colors();
+
+    this._valueKeywords = [
+        "above", "absolute", "activeborder", "activecaption", "afar", "after-white-space", "ahead", "alias", "all", "all-scroll",
+        "alternate", "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", "arabic-indic", "armenian", "asterisks",
+        "auto", "avoid", "background", "backwards", "baseline", "below", "bidi-override", "binary", "bengali", "blink",
+        "block", "block-axis", "bold", "bolder", "border", "border-box", "both", "bottom", "break-all", "break-word", "button",
+        "button-bevel", "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", "capitalize", "caps-lock-indicator",
+        "caption", "captiontext", "caret", "cell", "center", "checkbox", "circle", "cjk-earthly-branch", "cjk-heavenly-stem", "cjk-ideographic",
+        "clear", "clip", "close-quote", "col-resize", "collapse", "compact", "condensed", "contain", "content", "content-box", "context-menu",
+        "continuous", "copy", "cover", "crop", "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", "decimal-leading-zero", "default",
+        "default-button", "destination-atop", "destination-in", "destination-out", "destination-over", "devanagari", "disc", "discard", "document",
+        "dot-dash", "dot-dot-dash", "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", "element",
+        "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", "ethiopic-abegede-am-et", "ethiopic-abegede-gez",
+        "ethiopic-abegede-ti-er", "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", "ethiopic-halehame-aa-et",
+        "ethiopic-halehame-am-et", "ethiopic-halehame-gez", "ethiopic-halehame-om-et", "ethiopic-halehame-sid-et",
+        "ethiopic-halehame-so-et", "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig", "ew-resize", "expanded",
+        "extra-condensed", "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", "forwards", "from", "geometricPrecision",
+        "georgian", "graytext", "groove", "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", "help",
+        "hidden", "hide", "higher", "highlight", "highlighttext", "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
+        "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", "infobackground", "infotext", "inherit", "initial", "inline",
+        "inline-axis", "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", "italic", "justify", "kannada", "katakana",
+        "katakana-iroha", "khmer", "landscape", "lao", "large", "larger", "left", "level", "lighter", "line-through", "linear", "lines",
+        "list-item", "listbox", "listitem", "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", "lower-greek",
+        "lower-hexadecimal", "lower-latin", "lower-norwegian", "lower-roman", "lowercase", "ltr", "malayalam", "match", "media-controls-background",
+        "media-current-time-display", "media-fullscreen-button", "media-mute-button", "media-play-button", "media-return-to-realtime-button",
+        "media-rewind-button", "media-seek-back-button", "media-seek-forward-button", "media-slider", "media-sliderthumb", "media-time-remaining-display",
+        "media-volume-slider", "media-volume-slider-container", "media-volume-sliderthumb", "medium", "menu", "menulist", "menulist-button",
+        "menulist-text", "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", "mix", "mongolian", "monospace", "move", "multiple",
+        "myanmar", "n-resize", "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none",
+        "normal", "not-allowed", "nowrap", "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", "optimizeLegibility",
+        "optimizeSpeed", "oriya", "oromo", "outset", "outside", "overlay", "overline", "padding", "padding-box", "painted", "paused",
+        "persian", "plus-darker", "plus-lighter", "pointer", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress",
+        "push-button", "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", "repeat", "repeat-x",
+        "repeat-y", "reset", "reverse", "rgb", "rgba", "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", "s-resize", "sans-serif",
+        "scroll", "scrollbar", "se-resize", "searchfield", "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
+        "searchfield-results-decoration", "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", "single",
+        "skip-white-space", "slide", "slider-horizontal", "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
+        "small", "small-caps", "small-caption", "smaller", "solid", "somali", "source-atop", "source-in", "source-out", "source-over",
+        "space", "square", "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub", "subpixel-antialiased", "super",
+        "sw-resize", "table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group",
+        "table-row", "table-row-group", "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", "thick", "thin",
+        "threeddarkshadow", "threedface", "threedhighlight", "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", "tigrinya-er-abegede",
+        "tigrinya-et", "tigrinya-et-abegede", "to", "top", "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", "upper-alpha", "upper-armenian",
+        "upper-greek", "upper-hexadecimal", "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", "vertical", "vertical-text", "visible",
+        "visibleFill", "visiblePainted", "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", "window", "windowframe", "windowtext",
+        "x-large", "x-small", "xor", "xx-large", "xx-small", "yellow", "-wap-marquee", "-webkit-activelink", "-webkit-auto", "-webkit-baseline-middle",
+        "-webkit-body", "-webkit-box", "-webkit-center", "-webkit-control", "-webkit-focus-ring-color", "-webkit-grab", "-webkit-grabbing",
+        "-webkit-gradient", "-webkit-inline-box", "-webkit-left", "-webkit-link", "-webkit-marquee", "-webkit-mini-control", "-webkit-nowrap", "-webkit-pictograph",
+        "-webkit-right", "-webkit-small-control", "-webkit-text", "-webkit-xxx-large", "-webkit-zoom-in", "-webkit-zoom-out",
+    ].keySet();
+
+    this._scssValueKeywords = [
+        "abs", "adjust-color", "adjust-hue", "alpha", "append", "ceil", "change-color", "comparable", "complement", "darken", "desaturate",
+        "fade-in", "fade-out", "floor", "grayscale", "hue", "ie-hex-str", "invert", "join", "length", "lighten",
+        "lightness", "max", "min", "mix", "nth", "opacify", "opacity", "percentage", "quote", "round", "saturate",
+        "saturation", "scale-color", "transparentize", "type-of", "unit", "unitless", "unquote", "zip"
+    ].keySet();
+
+    this._lexConditions = {
+        INITIAL: 0,
+        COMMENT: 1,
+        DSTRING: 2,
+        SSTRING: 3
+    };
+
+    this._parseConditions = {
+        INITIAL: 0,
+        PROPERTY: 1,
+        PROPERTY_VALUE: 2,
+        AT_RULE: 3,
+        AT_MEDIA_RULE: 4
+    };
+
+    this.case_INITIAL = 1000;
+    this.case_COMMENT = 1002;
+    this.case_DSTRING = 1003;
+    this.case_SSTRING = 1004;
+
+    this.condition = this.createInitialCondition();
+}
+
+WebInspector.SourceCSSTokenizer.SCSSAtRelatedKeywords = ["from", "if", "in", "through"].keySet();
+
+WebInspector.SourceCSSTokenizer.MediaTypes = ["all", "aural", "braille", "embossed", "handheld", "import", "print", "projection", "screen", "tty", "tv"].keySet();
+
+WebInspector.SourceCSSTokenizer.prototype = {
+    createInitialCondition: function()
+    {
+        return { lexCondition: this._lexConditions.INITIAL, parseCondition: this._parseConditions.INITIAL };
+    },
+
+    /**
+     * @param {boolean=} stringEnds
+     */
+    _stringToken: function(cursor, stringEnds)
+    {
+        if (this._isPropertyValue())
+            this.tokenType = "css-string";
+        else
+            this.tokenType = null;
+        return cursor;
+    },
+
+    _isPropertyValue: function()
+    {
+        return this._condition.parseCondition === this._parseConditions.PROPERTY_VALUE || this._condition.parseCondition === this._parseConditions.AT_RULE;
+    },
+
+    _setParseCondition: function(condition)
+    {
+        this._condition.parseCondition = condition;
+    },
+
+    nextToken: function(cursor)
+    {
+        var cursorOnEnter = cursor;
+        var gotoCase = 1;
+        var YYMARKER;
+        while (1) {
+            switch (gotoCase)
+            // Following comment is replaced with generated state machine.
+            
+        {
+            case 1: var yych;
+            var yyaccept = 0;
+            if (this.getLexCondition() < 2) {
+                if (this.getLexCondition() < 1) {
+                    { gotoCase = this.case_INITIAL; continue; };
+                } else {
+                    { gotoCase = this.case_COMMENT; continue; };
+                }
+            } else {
+                if (this.getLexCondition() < 3) {
+                    { gotoCase = this.case_DSTRING; continue; };
+                } else {
+                    { gotoCase = this.case_SSTRING; continue; };
+                }
+            }
+/* *********************************** */
+case this.case_COMMENT:
+
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 4; continue; };
+                { gotoCase = 3; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 4; continue; };
+                if (yych == '*') { gotoCase = 6; continue; };
+                { gotoCase = 3; continue; };
+            }
+case 2:
+            { this.tokenType = "css-comment"; return cursor; }
+case 3:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 12; continue; };
+case 4:
+            ++cursor;
+            { this.tokenType = null; return cursor; }
+case 6:
+            yyaccept = 1;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == '*') { gotoCase = 9; continue; };
+            if (yych != '/') { gotoCase = 11; continue; };
+case 7:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.INITIAL);
+            { this.tokenType = "css-comment"; return cursor; }
+case 9:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '*') { gotoCase = 9; continue; };
+            if (yych == '/') { gotoCase = 7; continue; };
+case 11:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 12:
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 2; continue; };
+                { gotoCase = 11; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 2; continue; };
+                if (yych == '*') { gotoCase = 9; continue; };
+                { gotoCase = 11; continue; };
+            }
+/* *********************************** */
+case this.case_DSTRING:
+            yych = this._charAt(cursor);
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 17; continue; };
+                if (yych <= '\f') { gotoCase = 16; continue; };
+                { gotoCase = 17; continue; };
+            } else {
+                if (yych <= '"') {
+                    if (yych <= '!') { gotoCase = 16; continue; };
+                    { gotoCase = 19; continue; };
+                } else {
+                    if (yych == '\\') { gotoCase = 21; continue; };
+                    { gotoCase = 16; continue; };
+                }
+            }
+case 15:
+            { return this._stringToken(cursor); }
+case 16:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 23; continue; };
+case 17:
+            ++cursor;
+case 18:
+            { this.tokenType = null; return cursor; }
+case 19:
+            ++cursor;
+case 20:
+            this.setLexCondition(this._lexConditions.INITIAL);
+            { return this._stringToken(cursor, true); }
+case 21:
+            yych = this._charAt(++cursor);
+            if (yych <= 'e') {
+                if (yych <= '\'') {
+                    if (yych == '"') { gotoCase = 22; continue; };
+                    if (yych <= '&') { gotoCase = 18; continue; };
+                } else {
+                    if (yych <= '\\') {
+                        if (yych <= '[') { gotoCase = 18; continue; };
+                    } else {
+                        if (yych != 'b') { gotoCase = 18; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'r') {
+                    if (yych <= 'm') {
+                        if (yych >= 'g') { gotoCase = 18; continue; };
+                    } else {
+                        if (yych <= 'n') { gotoCase = 22; continue; };
+                        if (yych <= 'q') { gotoCase = 18; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych <= 's') { gotoCase = 18; continue; };
+                    } else {
+                        if (yych != 'v') { gotoCase = 18; continue; };
+                    }
+                }
+            }
+case 22:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 23:
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 15; continue; };
+                if (yych <= '\f') { gotoCase = 22; continue; };
+                { gotoCase = 15; continue; };
+            } else {
+                if (yych <= '"') {
+                    if (yych <= '!') { gotoCase = 22; continue; };
+                    { gotoCase = 26; continue; };
+                } else {
+                    if (yych != '\\') { gotoCase = 22; continue; };
+                }
+            }
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'e') {
+                if (yych <= '\'') {
+                    if (yych == '"') { gotoCase = 22; continue; };
+                    if (yych >= '\'') { gotoCase = 22; continue; };
+                } else {
+                    if (yych <= '\\') {
+                        if (yych >= '\\') { gotoCase = 22; continue; };
+                    } else {
+                        if (yych == 'b') { gotoCase = 22; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'r') {
+                    if (yych <= 'm') {
+                        if (yych <= 'f') { gotoCase = 22; continue; };
+                    } else {
+                        if (yych <= 'n') { gotoCase = 22; continue; };
+                        if (yych >= 'r') { gotoCase = 22; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych >= 't') { gotoCase = 22; continue; };
+                    } else {
+                        if (yych == 'v') { gotoCase = 22; continue; };
+                    }
+                }
+            }
+            cursor = YYMARKER;
+            { gotoCase = 15; continue; };
+case 26:
+            ++cursor;
+            yych = this._charAt(cursor);
+            { gotoCase = 20; continue; };
+/* *********************************** */
+case this.case_INITIAL:
+            yych = this._charAt(cursor);
+            if (yych <= ':') {
+                if (yych <= '&') {
+                    if (yych <= '"') {
+                        if (yych <= ' ') { gotoCase = 29; continue; };
+                        if (yych <= '!') { gotoCase = 31; continue; };
+                        { gotoCase = 33; continue; };
+                    } else {
+                        if (yych <= '#') { gotoCase = 34; continue; };
+                        if (yych <= '$') { gotoCase = 35; continue; };
+                        if (yych >= '&') { gotoCase = 31; continue; };
+                    }
+                } else {
+                    if (yych <= '-') {
+                        if (yych <= '\'') { gotoCase = 36; continue; };
+                        if (yych >= '-') { gotoCase = 37; continue; };
+                    } else {
+                        if (yych <= '.') { gotoCase = 38; continue; };
+                        if (yych <= '/') { gotoCase = 39; continue; };
+                        if (yych <= '9') { gotoCase = 40; continue; };
+                        { gotoCase = 42; continue; };
+                    }
+                }
+            } else {
+                if (yych <= ']') {
+                    if (yych <= '=') {
+                        if (yych <= ';') { gotoCase = 44; continue; };
+                        if (yych >= '=') { gotoCase = 31; continue; };
+                    } else {
+                        if (yych <= '?') { gotoCase = 29; continue; };
+                        if (yych != '\\') { gotoCase = 31; continue; };
+                    }
+                } else {
+                    if (yych <= 'z') {
+                        if (yych == '_') { gotoCase = 31; continue; };
+                        if (yych >= 'a') { gotoCase = 31; continue; };
+                    } else {
+                        if (yych <= '{') { gotoCase = 46; continue; };
+                        if (yych == '}') { gotoCase = 48; continue; };
+                    }
+                }
+            }
+case 29:
+            ++cursor;
+case 30:
+            { this.tokenType = null; return cursor; }
+case 31:
+            ++cursor;
+            yych = this._charAt(cursor);
+            { gotoCase = 51; continue; };
+case 32:
+            {
+                    var token = this._line.substring(cursorOnEnter, cursor);
+                    this.tokenType = null;
+                    if (this._condition.parseCondition === this._parseConditions.INITIAL || this._condition.parseCondition === this._parseConditions.PROPERTY) {
+                        if (token.charAt(0) === "@") {
+                            this.tokenType = "css-at-rule";
+                            this._setParseCondition(token === "@media" ? this._parseConditions.AT_MEDIA_RULE : this._parseConditions.AT_RULE);
+                            this._condition.atKeyword = token;
+                        } else if (this._condition.parseCondition === this._parseConditions.INITIAL)
+                            this.tokenType = "css-selector";
+                        else if (this._propertyKeywords.hasOwnProperty(token))
+                            this.tokenType = "css-property";
+                    } else if (this._condition.parseCondition === this._parseConditions.AT_MEDIA_RULE || this._condition.parseCondition === this._parseConditions.AT_RULE) {
+                        if (WebInspector.SourceCSSTokenizer.SCSSAtRelatedKeywords.hasOwnProperty(token))
+                            this.tokenType = "css-at-rule";
+                        else if (WebInspector.SourceCSSTokenizer.MediaTypes.hasOwnProperty(token))
+                            this.tokenType = "css-keyword";
+                    }
+                    if (this.tokenType)
+                        return cursor;
+
+                    if (this._isPropertyValue()) {
+                        var firstChar = token.charAt(0);
+                        if (firstChar === "$")
+                            this.tokenType = "scss-variable";
+                        else if (firstChar === "!")
+                            this.tokenType = "css-bang-keyword";
+                        else if (this._condition.atKeyword === "@extend")
+                            this.tokenType = "css-selector";
+                        else if (this._valueKeywords.hasOwnProperty(token) || this._scssValueKeywords.hasOwnProperty(token))
+                            this.tokenType = "css-keyword";
+                        else if (this._colorKeywords.hasOwnProperty(token)) {
+                            // FIXME: this does not convert tokens toLowerCase() for the sake of speed.
+                            this.tokenType = "css-color";
+                        }
+                    } else if (this._condition.parseCondition !== this._parseConditions.PROPERTY_VALUE)
+                        this.tokenType = "css-selector";
+                    return cursor;
+                }
+case 33:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= '.') {
+                if (yych <= '!') {
+                    if (yych <= '\f') {
+                        if (yych == '\n') { gotoCase = 32; continue; };
+                        { gotoCase = 132; continue; };
+                    } else {
+                        if (yych <= '\r') { gotoCase = 32; continue; };
+                        if (yych <= ' ') { gotoCase = 132; continue; };
+                        { gotoCase = 130; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '"') { gotoCase = 116; continue; };
+                        if (yych <= '%') { gotoCase = 132; continue; };
+                        { gotoCase = 130; continue; };
+                    } else {
+                        if (yych == '-') { gotoCase = 130; continue; };
+                        { gotoCase = 132; continue; };
+                    }
+                }
+            } else {
+                if (yych <= '\\') {
+                    if (yych <= '=') {
+                        if (yych <= '9') { gotoCase = 130; continue; };
+                        if (yych <= '<') { gotoCase = 132; continue; };
+                        { gotoCase = 130; continue; };
+                    } else {
+                        if (yych <= '?') { gotoCase = 132; continue; };
+                        if (yych <= '[') { gotoCase = 130; continue; };
+                        { gotoCase = 134; continue; };
+                    }
+                } else {
+                    if (yych <= '_') {
+                        if (yych == '^') { gotoCase = 132; continue; };
+                        { gotoCase = 130; continue; };
+                    } else {
+                        if (yych <= '`') { gotoCase = 132; continue; };
+                        if (yych <= 'z') { gotoCase = 130; continue; };
+                        { gotoCase = 132; continue; };
+                    }
+                }
+            }
+case 34:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 30; continue; };
+                if (yych <= '9') { gotoCase = 127; continue; };
+                { gotoCase = 30; continue; };
+            } else {
+                if (yych <= 'Z') { gotoCase = 127; continue; };
+                if (yych <= '`') { gotoCase = 30; continue; };
+                if (yych <= 'z') { gotoCase = 127; continue; };
+                { gotoCase = 30; continue; };
+            }
+case 35:
+            yych = this._charAt(++cursor);
+            if (yych <= '<') {
+                if (yych <= '\'') {
+                    if (yych <= ' ') { gotoCase = 30; continue; };
+                    if (yych <= '"') { gotoCase = 124; continue; };
+                    if (yych <= '%') { gotoCase = 30; continue; };
+                    { gotoCase = 124; continue; };
+                } else {
+                    if (yych <= '-') {
+                        if (yych <= ',') { gotoCase = 30; continue; };
+                        { gotoCase = 124; continue; };
+                    } else {
+                        if (yych <= '.') { gotoCase = 30; continue; };
+                        if (yych <= '9') { gotoCase = 124; continue; };
+                        { gotoCase = 30; continue; };
+                    }
+                }
+            } else {
+                if (yych <= ']') {
+                    if (yych <= '?') {
+                        if (yych <= '=') { gotoCase = 124; continue; };
+                        { gotoCase = 30; continue; };
+                    } else {
+                        if (yych == '\\') { gotoCase = 30; continue; };
+                        { gotoCase = 124; continue; };
+                    }
+                } else {
+                    if (yych <= '_') {
+                        if (yych <= '^') { gotoCase = 30; continue; };
+                        { gotoCase = 124; continue; };
+                    } else {
+                        if (yych <= '`') { gotoCase = 30; continue; };
+                        if (yych <= 'z') { gotoCase = 124; continue; };
+                        { gotoCase = 30; continue; };
+                    }
+                }
+            }
+case 36:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= '.') {
+                if (yych <= '"') {
+                    if (yych <= '\f') {
+                        if (yych == '\n') { gotoCase = 32; continue; };
+                        { gotoCase = 118; continue; };
+                    } else {
+                        if (yych <= '\r') { gotoCase = 32; continue; };
+                        if (yych <= ' ') { gotoCase = 118; continue; };
+                        { gotoCase = 114; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '%') { gotoCase = 118; continue; };
+                        if (yych <= '&') { gotoCase = 114; continue; };
+                        { gotoCase = 116; continue; };
+                    } else {
+                        if (yych == '-') { gotoCase = 114; continue; };
+                        { gotoCase = 118; continue; };
+                    }
+                }
+            } else {
+                if (yych <= '\\') {
+                    if (yych <= '=') {
+                        if (yych <= '9') { gotoCase = 114; continue; };
+                        if (yych <= '<') { gotoCase = 118; continue; };
+                        { gotoCase = 114; continue; };
+                    } else {
+                        if (yych <= '?') { gotoCase = 118; continue; };
+                        if (yych <= '[') { gotoCase = 114; continue; };
+                        { gotoCase = 120; continue; };
+                    }
+                } else {
+                    if (yych <= '_') {
+                        if (yych == '^') { gotoCase = 118; continue; };
+                        { gotoCase = 114; continue; };
+                    } else {
+                        if (yych <= '`') { gotoCase = 118; continue; };
+                        if (yych <= 'z') { gotoCase = 114; continue; };
+                        { gotoCase = 118; continue; };
+                    }
+                }
+            }
+case 37:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == '.') { gotoCase = 67; continue; };
+            if (yych <= '/') { gotoCase = 51; continue; };
+            if (yych <= '9') { gotoCase = 52; continue; };
+            { gotoCase = 51; continue; };
+case 38:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 30; continue; };
+            if (yych <= '9') { gotoCase = 70; continue; };
+            { gotoCase = 30; continue; };
+case 39:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == '*') { gotoCase = 106; continue; };
+            { gotoCase = 51; continue; };
+case 40:
+            yyaccept = 1;
+            yych = this._charAt(YYMARKER = ++cursor);
+            switch (yych) {
+            case '!':
+            case '"':
+            case '&':
+            case '\'':
+            case '-':
+            case '/':
+            case '=':
+            case '@':
+            case 'A':
+            case 'B':
+            case 'C':
+            case 'D':
+            case 'E':
+            case 'F':
+            case 'G':
+            case 'I':
+            case 'J':
+            case 'K':
+            case 'L':
+            case 'M':
+            case 'N':
+            case 'O':
+            case 'P':
+            case 'Q':
+            case 'R':
+            case 'S':
+            case 'T':
+            case 'U':
+            case 'V':
+            case 'W':
+            case 'X':
+            case 'Y':
+            case 'Z':
+            case '[':
+            case ']':
+            case 'a':
+            case 'b':
+            case 'f':
+            case 'h':
+            case 'j':
+            case 'l':
+            case 'n':
+            case 'o':
+            case 'q':
+            case 'u':
+            case 'v':
+            case 'w':
+            case 'x':
+            case 'y':
+            case 'z':    { gotoCase = 50; continue; };
+            case '%':    { gotoCase = 69; continue; };
+            case '.':    { gotoCase = 67; continue; };
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':    { gotoCase = 52; continue; };
+            case 'H':    { gotoCase = 54; continue; };
+            case '_':    { gotoCase = 55; continue; };
+            case 'c':    { gotoCase = 56; continue; };
+            case 'd':    { gotoCase = 57; continue; };
+            case 'e':    { gotoCase = 58; continue; };
+            case 'g':    { gotoCase = 59; continue; };
+            case 'i':    { gotoCase = 60; continue; };
+            case 'k':    { gotoCase = 61; continue; };
+            case 'm':    { gotoCase = 62; continue; };
+            case 'p':    { gotoCase = 63; continue; };
+            case 'r':    { gotoCase = 64; continue; };
+            case 's':    { gotoCase = 65; continue; };
+            case 't':    { gotoCase = 66; continue; };
+            default:    { gotoCase = 41; continue; };
+            }
+case 41:
+            {
+                    if (this._isPropertyValue())
+                        this.tokenType = "css-number";
+                    else
+                        this.tokenType = null;
+                    return cursor;
+                }
+case 42:
+            ++cursor;
+            {
+                    this.tokenType = null;
+                    if (this._condition.parseCondition === this._parseConditions.PROPERTY || this._condition.parseCondition === this._parseConditions.INITIAL)
+                        this._setParseCondition(this._parseConditions.PROPERTY_VALUE);
+                    return cursor;
+                }
+case 44:
+            ++cursor;
+            {
+                    this.tokenType = null;
+                    this._setParseCondition(this._condition.openBraces ? this._parseConditions.PROPERTY : this._parseConditions.INITIAL);
+                    delete this._condition.atKeyword;
+                    return cursor;
+                }
+case 46:
+            ++cursor;
+            {
+                    this.tokenType = "block-start";
+                    this._condition.openBraces = (this._condition.openBraces || 0) + 1;
+                    if (this._condition.parseCondition === this._parseConditions.AT_MEDIA_RULE)
+                        this._setParseCondition(this._parseConditions.INITIAL);
+                    else
+                        this._setParseCondition(this._parseConditions.PROPERTY);
+                    return cursor;
+                }
+case 48:
+            ++cursor;
+            {
+                    this.tokenType = "block-end";
+                    if (this._condition.openBraces > 0)
+                        --this._condition.openBraces;
+                    this._setParseCondition(this._condition.openBraces ? this._parseConditions.PROPERTY : this._parseConditions.INITIAL);
+                    delete this._condition.atKeyword;
+                    return cursor;
+                }
+case 50:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 51:
+            if (yych <= '<') {
+                if (yych <= '\'') {
+                    if (yych <= ' ') { gotoCase = 32; continue; };
+                    if (yych <= '"') { gotoCase = 50; continue; };
+                    if (yych <= '%') { gotoCase = 32; continue; };
+                    { gotoCase = 50; continue; };
+                } else {
+                    if (yych <= '-') {
+                        if (yych <= ',') { gotoCase = 32; continue; };
+                        { gotoCase = 50; continue; };
+                    } else {
+                        if (yych <= '.') { gotoCase = 32; continue; };
+                        if (yych <= '9') { gotoCase = 50; continue; };
+                        { gotoCase = 32; continue; };
+                    }
+                }
+            } else {
+                if (yych <= ']') {
+                    if (yych <= '?') {
+                        if (yych <= '=') { gotoCase = 50; continue; };
+                        { gotoCase = 32; continue; };
+                    } else {
+                        if (yych == '\\') { gotoCase = 32; continue; };
+                        { gotoCase = 50; continue; };
+                    }
+                } else {
+                    if (yych <= '_') {
+                        if (yych <= '^') { gotoCase = 32; continue; };
+                        { gotoCase = 50; continue; };
+                    } else {
+                        if (yych <= '`') { gotoCase = 32; continue; };
+                        if (yych <= 'z') { gotoCase = 50; continue; };
+                        { gotoCase = 32; continue; };
+                    }
+                }
+            }
+case 52:
+            yyaccept = 1;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            switch (yych) {
+            case '!':
+            case '"':
+            case '&':
+            case '\'':
+            case '-':
+            case '/':
+            case '=':
+            case '@':
+            case 'A':
+            case 'B':
+            case 'C':
+            case 'D':
+            case 'E':
+            case 'F':
+            case 'G':
+            case 'I':
+            case 'J':
+            case 'K':
+            case 'L':
+            case 'M':
+            case 'N':
+            case 'O':
+            case 'P':
+            case 'Q':
+            case 'R':
+            case 'S':
+            case 'T':
+            case 'U':
+            case 'V':
+            case 'W':
+            case 'X':
+            case 'Y':
+            case 'Z':
+            case '[':
+            case ']':
+            case 'a':
+            case 'b':
+            case 'f':
+            case 'h':
+            case 'j':
+            case 'l':
+            case 'n':
+            case 'o':
+            case 'q':
+            case 'u':
+            case 'v':
+            case 'w':
+            case 'x':
+            case 'y':
+            case 'z':    { gotoCase = 50; continue; };
+            case '%':    { gotoCase = 69; continue; };
+            case '.':    { gotoCase = 67; continue; };
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':    { gotoCase = 52; continue; };
+            case 'H':    { gotoCase = 54; continue; };
+            case '_':    { gotoCase = 55; continue; };
+            case 'c':    { gotoCase = 56; continue; };
+            case 'd':    { gotoCase = 57; continue; };
+            case 'e':    { gotoCase = 58; continue; };
+            case 'g':    { gotoCase = 59; continue; };
+            case 'i':    { gotoCase = 60; continue; };
+            case 'k':    { gotoCase = 61; continue; };
+            case 'm':    { gotoCase = 62; continue; };
+            case 'p':    { gotoCase = 63; continue; };
+            case 'r':    { gotoCase = 64; continue; };
+            case 's':    { gotoCase = 65; continue; };
+            case 't':    { gotoCase = 66; continue; };
+            default:    { gotoCase = 41; continue; };
+            }
+case 54:
+            yych = this._charAt(++cursor);
+            if (yych == 'z') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 55:
+            yych = this._charAt(++cursor);
+            if (yych == '_') { gotoCase = 103; continue; };
+            { gotoCase = 51; continue; };
+case 56:
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 57:
+            yych = this._charAt(++cursor);
+            if (yych == 'e') { gotoCase = 102; continue; };
+            { gotoCase = 51; continue; };
+case 58:
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 65; continue; };
+            if (yych == 'x') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 59:
+            yych = this._charAt(++cursor);
+            if (yych == 'r') { gotoCase = 100; continue; };
+            { gotoCase = 51; continue; };
+case 60:
+            yych = this._charAt(++cursor);
+            if (yych == 'n') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 61:
+            yych = this._charAt(++cursor);
+            if (yych == 'H') { gotoCase = 99; continue; };
+            { gotoCase = 51; continue; };
+case 62:
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 65; continue; };
+            if (yych == 's') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 63:
+            yych = this._charAt(++cursor);
+            if (yych <= 's') {
+                if (yych == 'c') { gotoCase = 65; continue; };
+                { gotoCase = 51; continue; };
+            } else {
+                if (yych <= 't') { gotoCase = 65; continue; };
+                if (yych == 'x') { gotoCase = 65; continue; };
+                { gotoCase = 51; continue; };
+            }
+case 64:
+            yych = this._charAt(++cursor);
+            if (yych == 'a') { gotoCase = 97; continue; };
+            if (yych == 'e') { gotoCase = 98; continue; };
+            { gotoCase = 51; continue; };
+case 65:
+            yych = this._charAt(++cursor);
+            if (yych <= '<') {
+                if (yych <= '\'') {
+                    if (yych <= ' ') { gotoCase = 41; continue; };
+                    if (yych <= '"') { gotoCase = 50; continue; };
+                    if (yych <= '%') { gotoCase = 41; continue; };
+                    { gotoCase = 50; continue; };
+                } else {
+                    if (yych <= '-') {
+                        if (yych <= ',') { gotoCase = 41; continue; };
+                        { gotoCase = 50; continue; };
+                    } else {
+                        if (yych <= '.') { gotoCase = 41; continue; };
+                        if (yych <= '9') { gotoCase = 50; continue; };
+                        { gotoCase = 41; continue; };
+                    }
+                }
+            } else {
+                if (yych <= ']') {
+                    if (yych <= '?') {
+                        if (yych <= '=') { gotoCase = 50; continue; };
+                        { gotoCase = 41; continue; };
+                    } else {
+                        if (yych == '\\') { gotoCase = 41; continue; };
+                        { gotoCase = 50; continue; };
+                    }
+                } else {
+                    if (yych <= '_') {
+                        if (yych <= '^') { gotoCase = 41; continue; };
+                        { gotoCase = 50; continue; };
+                    } else {
+                        if (yych <= '`') { gotoCase = 41; continue; };
+                        if (yych <= 'z') { gotoCase = 50; continue; };
+                        { gotoCase = 41; continue; };
+                    }
+                }
+            }
+case 66:
+            yych = this._charAt(++cursor);
+            if (yych == 'u') { gotoCase = 95; continue; };
+            { gotoCase = 51; continue; };
+case 67:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 68; continue; };
+            if (yych <= '9') { gotoCase = 70; continue; };
+case 68:
+            cursor = YYMARKER;
+            if (yyaccept <= 0) {
+                { gotoCase = 32; continue; };
+            } else {
+                { gotoCase = 41; continue; };
+            }
+case 69:
+            yych = this._charAt(++cursor);
+            { gotoCase = 41; continue; };
+case 70:
+            yyaccept = 1;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'f') {
+                if (yych <= 'H') {
+                    if (yych <= '/') {
+                        if (yych == '%') { gotoCase = 69; continue; };
+                        { gotoCase = 41; continue; };
+                    } else {
+                        if (yych <= '9') { gotoCase = 70; continue; };
+                        if (yych <= 'G') { gotoCase = 41; continue; };
+                        { gotoCase = 82; continue; };
+                    }
+                } else {
+                    if (yych <= 'b') {
+                        if (yych == '_') { gotoCase = 74; continue; };
+                        { gotoCase = 41; continue; };
+                    } else {
+                        if (yych <= 'c') { gotoCase = 76; continue; };
+                        if (yych <= 'd') { gotoCase = 79; continue; };
+                        if (yych >= 'f') { gotoCase = 41; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'm') {
+                    if (yych <= 'i') {
+                        if (yych <= 'g') { gotoCase = 80; continue; };
+                        if (yych <= 'h') { gotoCase = 41; continue; };
+                        { gotoCase = 78; continue; };
+                    } else {
+                        if (yych == 'k') { gotoCase = 83; continue; };
+                        if (yych <= 'l') { gotoCase = 41; continue; };
+                        { gotoCase = 77; continue; };
+                    }
+                } else {
+                    if (yych <= 'q') {
+                        if (yych == 'p') { gotoCase = 75; continue; };
+                        { gotoCase = 41; continue; };
+                    } else {
+                        if (yych <= 'r') { gotoCase = 73; continue; };
+                        if (yych <= 's') { gotoCase = 69; continue; };
+                        if (yych <= 't') { gotoCase = 81; continue; };
+                        { gotoCase = 41; continue; };
+                    }
+                }
+            }
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 69; continue; };
+            if (yych == 'x') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 73:
+            yych = this._charAt(++cursor);
+            if (yych == 'a') { gotoCase = 93; continue; };
+            if (yych == 'e') { gotoCase = 94; continue; };
+            { gotoCase = 68; continue; };
+case 74:
+            yych = this._charAt(++cursor);
+            if (yych == '_') { gotoCase = 90; continue; };
+            { gotoCase = 68; continue; };
+case 75:
+            yych = this._charAt(++cursor);
+            if (yych <= 's') {
+                if (yych == 'c') { gotoCase = 69; continue; };
+                { gotoCase = 68; continue; };
+            } else {
+                if (yych <= 't') { gotoCase = 69; continue; };
+                if (yych == 'x') { gotoCase = 69; continue; };
+                { gotoCase = 68; continue; };
+            }
+case 76:
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 77:
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 69; continue; };
+            if (yych == 's') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 78:
+            yych = this._charAt(++cursor);
+            if (yych == 'n') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 79:
+            yych = this._charAt(++cursor);
+            if (yych == 'e') { gotoCase = 89; continue; };
+            { gotoCase = 68; continue; };
+case 80:
+            yych = this._charAt(++cursor);
+            if (yych == 'r') { gotoCase = 87; continue; };
+            { gotoCase = 68; continue; };
+case 81:
+            yych = this._charAt(++cursor);
+            if (yych == 'u') { gotoCase = 85; continue; };
+            { gotoCase = 68; continue; };
+case 82:
+            yych = this._charAt(++cursor);
+            if (yych == 'z') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 83:
+            yych = this._charAt(++cursor);
+            if (yych != 'H') { gotoCase = 68; continue; };
+            yych = this._charAt(++cursor);
+            if (yych == 'z') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 85:
+            yych = this._charAt(++cursor);
+            if (yych != 'r') { gotoCase = 68; continue; };
+            yych = this._charAt(++cursor);
+            if (yych == 'n') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 87:
+            yych = this._charAt(++cursor);
+            if (yych != 'a') { gotoCase = 68; continue; };
+            yych = this._charAt(++cursor);
+            if (yych == 'd') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 89:
+            yych = this._charAt(++cursor);
+            if (yych == 'g') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 90:
+            yych = this._charAt(++cursor);
+            if (yych != 'q') { gotoCase = 68; continue; };
+            yych = this._charAt(++cursor);
+            if (yych != 'e') { gotoCase = 68; continue; };
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 93:
+            yych = this._charAt(++cursor);
+            if (yych == 'd') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 94:
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 69; continue; };
+            { gotoCase = 68; continue; };
+case 95:
+            yych = this._charAt(++cursor);
+            if (yych != 'r') { gotoCase = 51; continue; };
+            yych = this._charAt(++cursor);
+            if (yych == 'n') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 97:
+            yych = this._charAt(++cursor);
+            if (yych == 'd') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 98:
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 99:
+            yych = this._charAt(++cursor);
+            if (yych == 'z') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 100:
+            yych = this._charAt(++cursor);
+            if (yych != 'a') { gotoCase = 51; continue; };
+            yych = this._charAt(++cursor);
+            if (yych == 'd') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 102:
+            yych = this._charAt(++cursor);
+            if (yych == 'g') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 103:
+            yych = this._charAt(++cursor);
+            if (yych != 'q') { gotoCase = 51; continue; };
+            yych = this._charAt(++cursor);
+            if (yych != 'e') { gotoCase = 51; continue; };
+            yych = this._charAt(++cursor);
+            if (yych == 'm') { gotoCase = 65; continue; };
+            { gotoCase = 51; continue; };
+case 106:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 110; continue; };
+                { gotoCase = 106; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 110; continue; };
+                if (yych != '*') { gotoCase = 106; continue; };
+            }
+case 108:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '*') { gotoCase = 108; continue; };
+            if (yych == '/') { gotoCase = 112; continue; };
+            { gotoCase = 106; continue; };
+case 110:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.COMMENT);
+            { this.tokenType = "css-comment"; return cursor; }
+case 112:
+            ++cursor;
+            { this.tokenType = "css-comment"; return cursor; }
+case 114:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '.') {
+                if (yych <= '"') {
+                    if (yych <= '\f') {
+                        if (yych == '\n') { gotoCase = 32; continue; };
+                        { gotoCase = 118; continue; };
+                    } else {
+                        if (yych <= '\r') { gotoCase = 32; continue; };
+                        if (yych <= ' ') { gotoCase = 118; continue; };
+                        { gotoCase = 114; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '%') { gotoCase = 118; continue; };
+                        if (yych <= '&') { gotoCase = 114; continue; };
+                    } else {
+                        if (yych == '-') { gotoCase = 114; continue; };
+                        { gotoCase = 118; continue; };
+                    }
+                }
+            } else {
+                if (yych <= '\\') {
+                    if (yych <= '=') {
+                        if (yych <= '9') { gotoCase = 114; continue; };
+                        if (yych <= '<') { gotoCase = 118; continue; };
+                        { gotoCase = 114; continue; };
+                    } else {
+                        if (yych <= '?') { gotoCase = 118; continue; };
+                        if (yych <= '[') { gotoCase = 114; continue; };
+                        { gotoCase = 120; continue; };
+                    }
+                } else {
+                    if (yych <= '_') {
+                        if (yych == '^') { gotoCase = 118; continue; };
+                        { gotoCase = 114; continue; };
+                    } else {
+                        if (yych <= '`') { gotoCase = 118; continue; };
+                        if (yych <= 'z') { gotoCase = 114; continue; };
+                        { gotoCase = 118; continue; };
+                    }
+                }
+            }
+case 116:
+            ++cursor;
+            if ((yych = this._charAt(cursor)) <= '<') {
+                if (yych <= '\'') {
+                    if (yych <= ' ') { gotoCase = 117; continue; };
+                    if (yych <= '"') { gotoCase = 50; continue; };
+                    if (yych >= '&') { gotoCase = 50; continue; };
+                } else {
+                    if (yych <= '-') {
+                        if (yych >= '-') { gotoCase = 50; continue; };
+                    } else {
+                        if (yych <= '.') { gotoCase = 117; continue; };
+                        if (yych <= '9') { gotoCase = 50; continue; };
+                    }
+                }
+            } else {
+                if (yych <= ']') {
+                    if (yych <= '?') {
+                        if (yych <= '=') { gotoCase = 50; continue; };
+                    } else {
+                        if (yych != '\\') { gotoCase = 50; continue; };
+                    }
+                } else {
+                    if (yych <= '_') {
+                        if (yych >= '_') { gotoCase = 50; continue; };
+                    } else {
+                        if (yych <= '`') { gotoCase = 117; continue; };
+                        if (yych <= 'z') { gotoCase = 50; continue; };
+                    }
+                }
+            }
+case 117:
+            { return this._stringToken(cursor, true); }
+case 118:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 68; continue; };
+                if (yych <= '\f') { gotoCase = 118; continue; };
+                { gotoCase = 68; continue; };
+            } else {
+                if (yych <= '\'') {
+                    if (yych <= '&') { gotoCase = 118; continue; };
+                    { gotoCase = 123; continue; };
+                } else {
+                    if (yych != '\\') { gotoCase = 118; continue; };
+                }
+            }
+case 120:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'a') {
+                if (yych <= '!') {
+                    if (yych <= '\n') {
+                        if (yych <= '\t') { gotoCase = 68; continue; };
+                    } else {
+                        if (yych != '\r') { gotoCase = 68; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '"') { gotoCase = 118; continue; };
+                        if (yych <= '&') { gotoCase = 68; continue; };
+                        { gotoCase = 118; continue; };
+                    } else {
+                        if (yych == '\\') { gotoCase = 118; continue; };
+                        { gotoCase = 68; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'q') {
+                    if (yych <= 'f') {
+                        if (yych <= 'b') { gotoCase = 118; continue; };
+                        if (yych <= 'e') { gotoCase = 68; continue; };
+                        { gotoCase = 118; continue; };
+                    } else {
+                        if (yych == 'n') { gotoCase = 118; continue; };
+                        { gotoCase = 68; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych == 's') { gotoCase = 68; continue; };
+                        { gotoCase = 118; continue; };
+                    } else {
+                        if (yych == 'v') { gotoCase = 118; continue; };
+                        { gotoCase = 68; continue; };
+                    }
+                }
+            }
+            ++cursor;
+            this.setLexCondition(this._lexConditions.SSTRING);
+            { return this._stringToken(cursor); }
+case 123:
+            yych = this._charAt(++cursor);
+            { gotoCase = 117; continue; };
+case 124:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '<') {
+                if (yych <= '\'') {
+                    if (yych <= ' ') { gotoCase = 126; continue; };
+                    if (yych <= '"') { gotoCase = 124; continue; };
+                    if (yych >= '&') { gotoCase = 124; continue; };
+                } else {
+                    if (yych <= '-') {
+                        if (yych >= '-') { gotoCase = 124; continue; };
+                    } else {
+                        if (yych <= '.') { gotoCase = 126; continue; };
+                        if (yych <= '9') { gotoCase = 124; continue; };
+                    }
+                }
+            } else {
+                if (yych <= ']') {
+                    if (yych <= '?') {
+                        if (yych <= '=') { gotoCase = 124; continue; };
+                    } else {
+                        if (yych != '\\') { gotoCase = 124; continue; };
+                    }
+                } else {
+                    if (yych <= '_') {
+                        if (yych >= '_') { gotoCase = 124; continue; };
+                    } else {
+                        if (yych <= '`') { gotoCase = 126; continue; };
+                        if (yych <= 'z') { gotoCase = 124; continue; };
+                    }
+                }
+            }
+case 126:
+            {
+                    if (this._condition.parseCondition === this._condition.parseCondition.INITIAL || this._condition.parseCondition === this._condition.parseCondition.AT_RULE)
+                        this._setParseCondition(this._parseConditions.PROPERTY);
+                    this.tokenType = "scss-variable";
+                    return cursor;
+                }
+case 127:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 129; continue; };
+                if (yych <= '9') { gotoCase = 127; continue; };
+            } else {
+                if (yych <= 'Z') { gotoCase = 127; continue; };
+                if (yych <= '`') { gotoCase = 129; continue; };
+                if (yych <= 'z') { gotoCase = 127; continue; };
+            }
+case 129:
+            {
+                    if (this._isPropertyValue())
+                        this.tokenType = "css-color";
+                    else if (this._condition.parseCondition === this._parseConditions.INITIAL)
+                        this.tokenType = "css-selector";
+                    else
+                        this.tokenType = null;
+                    return cursor;
+                }
+case 130:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '.') {
+                if (yych <= '!') {
+                    if (yych <= '\f') {
+                        if (yych == '\n') { gotoCase = 32; continue; };
+                    } else {
+                        if (yych <= '\r') { gotoCase = 32; continue; };
+                        if (yych >= '!') { gotoCase = 130; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '"') { gotoCase = 116; continue; };
+                        if (yych >= '&') { gotoCase = 130; continue; };
+                    } else {
+                        if (yych == '-') { gotoCase = 130; continue; };
+                    }
+                }
+            } else {
+                if (yych <= '\\') {
+                    if (yych <= '=') {
+                        if (yych <= '9') { gotoCase = 130; continue; };
+                        if (yych >= '=') { gotoCase = 130; continue; };
+                    } else {
+                        if (yych <= '?') { gotoCase = 132; continue; };
+                        if (yych <= '[') { gotoCase = 130; continue; };
+                        { gotoCase = 134; continue; };
+                    }
+                } else {
+                    if (yych <= '_') {
+                        if (yych != '^') { gotoCase = 130; continue; };
+                    } else {
+                        if (yych <= '`') { gotoCase = 132; continue; };
+                        if (yych <= 'z') { gotoCase = 130; continue; };
+                    }
+                }
+            }
+case 132:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 68; continue; };
+                if (yych <= '\f') { gotoCase = 132; continue; };
+                { gotoCase = 68; continue; };
+            } else {
+                if (yych <= '"') {
+                    if (yych <= '!') { gotoCase = 132; continue; };
+                    { gotoCase = 123; continue; };
+                } else {
+                    if (yych != '\\') { gotoCase = 132; continue; };
+                }
+            }
+case 134:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'a') {
+                if (yych <= '!') {
+                    if (yych <= '\n') {
+                        if (yych <= '\t') { gotoCase = 68; continue; };
+                    } else {
+                        if (yych != '\r') { gotoCase = 68; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '"') { gotoCase = 132; continue; };
+                        if (yych <= '&') { gotoCase = 68; continue; };
+                        { gotoCase = 132; continue; };
+                    } else {
+                        if (yych == '\\') { gotoCase = 132; continue; };
+                        { gotoCase = 68; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'q') {
+                    if (yych <= 'f') {
+                        if (yych <= 'b') { gotoCase = 132; continue; };
+                        if (yych <= 'e') { gotoCase = 68; continue; };
+                        { gotoCase = 132; continue; };
+                    } else {
+                        if (yych == 'n') { gotoCase = 132; continue; };
+                        { gotoCase = 68; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych == 's') { gotoCase = 68; continue; };
+                        { gotoCase = 132; continue; };
+                    } else {
+                        if (yych == 'v') { gotoCase = 132; continue; };
+                        { gotoCase = 68; continue; };
+                    }
+                }
+            }
+            ++cursor;
+            this.setLexCondition(this._lexConditions.DSTRING);
+            { return this._stringToken(cursor); }
+/* *********************************** */
+case this.case_SSTRING:
+            yych = this._charAt(cursor);
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 141; continue; };
+                if (yych <= '\f') { gotoCase = 140; continue; };
+                { gotoCase = 141; continue; };
+            } else {
+                if (yych <= '\'') {
+                    if (yych <= '&') { gotoCase = 140; continue; };
+                    { gotoCase = 143; continue; };
+                } else {
+                    if (yych == '\\') { gotoCase = 145; continue; };
+                    { gotoCase = 140; continue; };
+                }
+            }
+case 139:
+            { return this._stringToken(cursor); }
+case 140:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 147; continue; };
+case 141:
+            ++cursor;
+case 142:
+            { this.tokenType = null; return cursor; }
+case 143:
+            ++cursor;
+case 144:
+            this.setLexCondition(this._lexConditions.INITIAL);
+            { return this._stringToken(cursor, true); }
+case 145:
+            yych = this._charAt(++cursor);
+            if (yych <= 'e') {
+                if (yych <= '\'') {
+                    if (yych == '"') { gotoCase = 146; continue; };
+                    if (yych <= '&') { gotoCase = 142; continue; };
+                } else {
+                    if (yych <= '\\') {
+                        if (yych <= '[') { gotoCase = 142; continue; };
+                    } else {
+                        if (yych != 'b') { gotoCase = 142; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'r') {
+                    if (yych <= 'm') {
+                        if (yych >= 'g') { gotoCase = 142; continue; };
+                    } else {
+                        if (yych <= 'n') { gotoCase = 146; continue; };
+                        if (yych <= 'q') { gotoCase = 142; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych <= 's') { gotoCase = 142; continue; };
+                    } else {
+                        if (yych != 'v') { gotoCase = 142; continue; };
+                    }
+                }
+            }
+case 146:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 147:
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 139; continue; };
+                if (yych <= '\f') { gotoCase = 146; continue; };
+                { gotoCase = 139; continue; };
+            } else {
+                if (yych <= '\'') {
+                    if (yych <= '&') { gotoCase = 146; continue; };
+                    { gotoCase = 150; continue; };
+                } else {
+                    if (yych != '\\') { gotoCase = 146; continue; };
+                }
+            }
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'e') {
+                if (yych <= '\'') {
+                    if (yych == '"') { gotoCase = 146; continue; };
+                    if (yych >= '\'') { gotoCase = 146; continue; };
+                } else {
+                    if (yych <= '\\') {
+                        if (yych >= '\\') { gotoCase = 146; continue; };
+                    } else {
+                        if (yych == 'b') { gotoCase = 146; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'r') {
+                    if (yych <= 'm') {
+                        if (yych <= 'f') { gotoCase = 146; continue; };
+                    } else {
+                        if (yych <= 'n') { gotoCase = 146; continue; };
+                        if (yych >= 'r') { gotoCase = 146; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych >= 't') { gotoCase = 146; continue; };
+                    } else {
+                        if (yych == 'v') { gotoCase = 146; continue; };
+                    }
+                }
+            }
+            cursor = YYMARKER;
+            { gotoCase = 139; continue; };
+case 150:
+            ++cursor;
+            yych = this._charAt(cursor);
+            { gotoCase = 144; continue; };
+        }
+
+        }
+    },
+
+    __proto__: WebInspector.SourceTokenizer.prototype
+}
diff --git a/Source/devtools/front_end/SourceCSSTokenizer.re2js b/Source/devtools/front_end/SourceCSSTokenizer.re2js
new file mode 100644
index 0000000..84c0e43
--- /dev/null
+++ b/Source/devtools/front_end/SourceCSSTokenizer.re2js
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+// Generate js file as follows:
+
+/*
+re2c -isc devtools/front_end/SourceCSSTokenizer.re2js \
+  | sed 's|^yy\([^:]*\)*\:|case \1:|' \
+  | sed 's|[*]cursor[+][+]|this._charAt(cursor++)|' \
+  | sed 's|[[*][+][+]cursor|this._charAt(++cursor)|' \
+  | sed 's|[*]cursor|this._charAt(cursor)|' \
+  | sed 's|yych = \*\([^;]*\)|yych = this._charAt\1|' \
+  | sed 's|goto case \([^;]*\)|{ gotoCase = \1; continue; }|' \
+  | sed 's|yych <= \(0x[0-9a-fA-F]*\)|yych \<\= String.fromCharCode(\1)|' \
+  | sed 's|unsigned\ int|var|' \
+  | sed 's|var\ yych|case 1: var yych|' > devtools/front_end/SourceCSSTokenizer.js
+*/
+
+/**
+ * @constructor
+ * @extends {WebInspector.SourceTokenizer}
+ */
+WebInspector.SourceCSSTokenizer = function()
+{
+    WebInspector.SourceTokenizer.call(this);
+
+    this._propertyKeywords = WebInspector.CSSMetadata.cssPropertiesMetainfoKeySet();
+    this._colorKeywords = WebInspector.CSSMetadata.colors();
+
+    this._valueKeywords = [
+        "above", "absolute", "activeborder", "activecaption", "afar", "after-white-space", "ahead", "alias", "all", "all-scroll",
+        "alternate", "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", "arabic-indic", "armenian", "asterisks",
+        "auto", "avoid", "background", "backwards", "baseline", "below", "bidi-override", "binary", "bengali", "blink",
+        "block", "block-axis", "bold", "bolder", "border", "border-box", "both", "bottom", "break-all", "break-word", "button",
+        "button-bevel", "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", "capitalize", "caps-lock-indicator",
+        "caption", "captiontext", "caret", "cell", "center", "checkbox", "circle", "cjk-earthly-branch", "cjk-heavenly-stem", "cjk-ideographic",
+        "clear", "clip", "close-quote", "col-resize", "collapse", "compact", "condensed", "contain", "content", "content-box", "context-menu",
+        "continuous", "copy", "cover", "crop", "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", "decimal-leading-zero", "default",
+        "default-button", "destination-atop", "destination-in", "destination-out", "destination-over", "devanagari", "disc", "discard", "document",
+        "dot-dash", "dot-dot-dash", "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", "element",
+        "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", "ethiopic-abegede-am-et", "ethiopic-abegede-gez",
+        "ethiopic-abegede-ti-er", "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", "ethiopic-halehame-aa-et",
+        "ethiopic-halehame-am-et", "ethiopic-halehame-gez", "ethiopic-halehame-om-et", "ethiopic-halehame-sid-et",
+        "ethiopic-halehame-so-et", "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig", "ew-resize", "expanded",
+        "extra-condensed", "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", "forwards", "from", "geometricPrecision",
+        "georgian", "graytext", "groove", "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", "help",
+        "hidden", "hide", "higher", "highlight", "highlighttext", "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
+        "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", "infobackground", "infotext", "inherit", "initial", "inline",
+        "inline-axis", "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", "italic", "justify", "kannada", "katakana",
+        "katakana-iroha", "khmer", "landscape", "lao", "large", "larger", "left", "level", "lighter", "line-through", "linear", "lines",
+        "list-item", "listbox", "listitem", "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", "lower-greek",
+        "lower-hexadecimal", "lower-latin", "lower-norwegian", "lower-roman", "lowercase", "ltr", "malayalam", "match", "media-controls-background",
+        "media-current-time-display", "media-fullscreen-button", "media-mute-button", "media-play-button", "media-return-to-realtime-button",
+        "media-rewind-button", "media-seek-back-button", "media-seek-forward-button", "media-slider", "media-sliderthumb", "media-time-remaining-display",
+        "media-volume-slider", "media-volume-slider-container", "media-volume-sliderthumb", "medium", "menu", "menulist", "menulist-button",
+        "menulist-text", "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", "mix", "mongolian", "monospace", "move", "multiple",
+        "myanmar", "n-resize", "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none",
+        "normal", "not-allowed", "nowrap", "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", "optimizeLegibility",
+        "optimizeSpeed", "oriya", "oromo", "outset", "outside", "overlay", "overline", "padding", "padding-box", "painted", "paused",
+        "persian", "plus-darker", "plus-lighter", "pointer", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress",
+        "push-button", "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", "repeat", "repeat-x",
+        "repeat-y", "reset", "reverse", "rgb", "rgba", "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", "s-resize", "sans-serif",
+        "scroll", "scrollbar", "se-resize", "searchfield", "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
+        "searchfield-results-decoration", "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", "single",
+        "skip-white-space", "slide", "slider-horizontal", "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
+        "small", "small-caps", "small-caption", "smaller", "solid", "somali", "source-atop", "source-in", "source-out", "source-over",
+        "space", "square", "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub", "subpixel-antialiased", "super",
+        "sw-resize", "table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group",
+        "table-row", "table-row-group", "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", "thick", "thin",
+        "threeddarkshadow", "threedface", "threedhighlight", "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", "tigrinya-er-abegede",
+        "tigrinya-et", "tigrinya-et-abegede", "to", "top", "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", "upper-alpha", "upper-armenian",
+        "upper-greek", "upper-hexadecimal", "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", "vertical", "vertical-text", "visible",
+        "visibleFill", "visiblePainted", "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", "window", "windowframe", "windowtext",
+        "x-large", "x-small", "xor", "xx-large", "xx-small", "yellow", "-wap-marquee", "-webkit-activelink", "-webkit-auto", "-webkit-baseline-middle",
+        "-webkit-body", "-webkit-box", "-webkit-center", "-webkit-control", "-webkit-focus-ring-color", "-webkit-grab", "-webkit-grabbing",
+        "-webkit-gradient", "-webkit-inline-box", "-webkit-left", "-webkit-link", "-webkit-marquee", "-webkit-mini-control", "-webkit-nowrap", "-webkit-pictograph",
+        "-webkit-right", "-webkit-small-control", "-webkit-text", "-webkit-xxx-large", "-webkit-zoom-in", "-webkit-zoom-out",
+    ].keySet();
+
+    this._scssValueKeywords = [
+        "abs", "adjust-color", "adjust-hue", "alpha", "append", "ceil", "change-color", "comparable", "complement", "darken", "desaturate",
+        "fade-in", "fade-out", "floor", "grayscale", "hue", "ie-hex-str", "invert", "join", "length", "lighten",
+        "lightness", "max", "min", "mix", "nth", "opacify", "opacity", "percentage", "quote", "round", "saturate",
+        "saturation", "scale-color", "transparentize", "type-of", "unit", "unitless", "unquote", "zip"
+    ].keySet();
+
+    this._lexConditions = {
+        INITIAL: 0,
+        COMMENT: 1,
+        DSTRING: 2,
+        SSTRING: 3
+    };
+
+    this._parseConditions = {
+        INITIAL: 0,
+        PROPERTY: 1,
+        PROPERTY_VALUE: 2,
+        AT_RULE: 3,
+        AT_MEDIA_RULE: 4
+    };
+
+    this.case_INITIAL = 1000;
+    this.case_COMMENT = 1002;
+    this.case_DSTRING = 1003;
+    this.case_SSTRING = 1004;
+
+    this.condition = this.createInitialCondition();
+}
+
+WebInspector.SourceCSSTokenizer.SCSSAtRelatedKeywords = ["from", "if", "in", "through"].keySet();
+
+WebInspector.SourceCSSTokenizer.MediaTypes = ["all", "aural", "braille", "embossed", "handheld", "import", "print", "projection", "screen", "tty", "tv"].keySet();
+
+WebInspector.SourceCSSTokenizer.prototype = {
+    createInitialCondition: function()
+    {
+        return { lexCondition: this._lexConditions.INITIAL, parseCondition: this._parseConditions.INITIAL };
+    },
+
+    /**
+     * @param {boolean=} stringEnds
+     */
+    _stringToken: function(cursor, stringEnds)
+    {
+        if (this._isPropertyValue())
+            this.tokenType = "css-string";
+        else
+            this.tokenType = null;
+        return cursor;
+    },
+
+    _isPropertyValue: function()
+    {
+        return this._condition.parseCondition === this._parseConditions.PROPERTY_VALUE || this._condition.parseCondition === this._parseConditions.AT_RULE;
+    },
+
+    _setParseCondition: function(condition)
+    {
+        this._condition.parseCondition = condition;
+    },
+
+    nextToken: function(cursor)
+    {
+        var cursorOnEnter = cursor;
+        var gotoCase = 1;
+        var YYMARKER;
+        while (1) {
+            switch (gotoCase)
+            // Following comment is replaced with generated state machine.
+            /*!re2c
+                re2c:define:YYCTYPE  = "var";
+                re2c:define:YYCURSOR = cursor;
+                re2c:define:YYGETCONDITION = "this.getLexCondition";
+                re2c:define:YYSETCONDITION = "this.setLexCondition";
+                re2c:condprefix = "case this.case_";
+                re2c:condenumprefix = "this._lexConditions.";
+                re2c:yyfill:enable = 0;
+                re2c:labelprefix = "case ";
+                re2c:indent:top = 2;
+                re2c:indent:string = "    ";
+
+                CommentContent = ([^*\r\n] | ("*"+[^/*]))*;
+                Comment = "/*" CommentContent "*"+ "/";
+                CommentStart = "/*" CommentContent [\r\n];
+                CommentEnd = CommentContent "*"+ "/";
+
+                OpenCurlyBracket = "{";
+                CloseCurlyBracket = "}";
+
+                Colon = ":";
+                Semicolon = ";";
+
+                NumericLiteral = "-"? ([0-9]+ | [0-9]* "." [0-9]+)  ("em" | "rem" | "__qem" | "ex" | "px" | "cm" |
+                    "mm" | "in" | "pt" | "pc" | "deg" | "rad" | "grad" | "turn" | "ms" | "s" | "Hz" | "kHz" | "%")?;
+
+                ColorOrSelector = "#" [0-9A-Za-z]+;
+                Identifier = [@!_\-0-9a-zA-Z\[\]='"/&]+;
+                Variable = "$" Identifier;
+
+                DoubleStringContent = ([^\r\n\"\\] | "\\" ['"\\bfnrtv])*;
+                SingleStringContent = ([^\r\n\'\\] | "\\" ['"\\bfnrtv])*;
+                StringLiteral = "\"" DoubleStringContent "\"" | "'" SingleStringContent "'";
+                DoubleStringStart = "\"" DoubleStringContent "\\" [\r\n];
+                DoubleStringEnd = DoubleStringContent "\"";
+                SingleStringStart = "'" SingleStringContent "\\" [\r\n];
+                SingleStringEnd = SingleStringContent "'";
+
+                <INITIAL> Comment { this.tokenType = "css-comment"; return cursor; }
+                <INITIAL> CommentStart => COMMENT { this.tokenType = "css-comment"; return cursor; }
+                <COMMENT> CommentContent => COMMENT { this.tokenType = "css-comment"; return cursor; }
+                <COMMENT> CommentEnd => INITIAL { this.tokenType = "css-comment"; return cursor; }
+
+                <INITIAL> StringLiteral { return this._stringToken(cursor, true); }
+                <INITIAL> DoubleStringStart => DSTRING { return this._stringToken(cursor); }
+                <DSTRING> DoubleStringContent => DSTRING { return this._stringToken(cursor); }
+                <DSTRING> DoubleStringEnd => INITIAL { return this._stringToken(cursor, true); }
+                <INITIAL> SingleStringStart => SSTRING { return this._stringToken(cursor); }
+                <SSTRING> SingleStringContent => SSTRING { return this._stringToken(cursor); }
+                <SSTRING> SingleStringEnd => INITIAL { return this._stringToken(cursor, true); }
+
+                <INITIAL> OpenCurlyBracket
+                {
+                    this.tokenType = "block-start";
+                    this._condition.openBraces = (this._condition.openBraces || 0) + 1;
+                    if (this._condition.parseCondition === this._parseConditions.AT_MEDIA_RULE)
+                        this._setParseCondition(this._parseConditions.INITIAL);
+                    else
+                        this._setParseCondition(this._parseConditions.PROPERTY);
+                    return cursor;
+                }
+
+                <INITIAL> CloseCurlyBracket
+                {
+                    this.tokenType = "block-end";
+                    if (this._condition.openBraces > 0)
+                        --this._condition.openBraces;
+                    this._setParseCondition(this._condition.openBraces ? this._parseConditions.PROPERTY : this._parseConditions.INITIAL);
+                    delete this._condition.atKeyword;
+                    return cursor;
+                }
+
+                <INITIAL> Colon
+                {
+                    this.tokenType = null;
+                    if (this._condition.parseCondition === this._parseConditions.PROPERTY || this._condition.parseCondition === this._parseConditions.INITIAL)
+                        this._setParseCondition(this._parseConditions.PROPERTY_VALUE);
+                    return cursor;
+                }
+
+                <INITIAL> Semicolon
+                {
+                    this.tokenType = null;
+                    this._setParseCondition(this._condition.openBraces ? this._parseConditions.PROPERTY : this._parseConditions.INITIAL);
+                    delete this._condition.atKeyword;
+                    return cursor;
+                }
+
+                <INITIAL> NumericLiteral
+                {
+                    if (this._isPropertyValue())
+                        this.tokenType = "css-number";
+                    else
+                        this.tokenType = null;
+                    return cursor;
+                }
+
+                <INITIAL> ColorOrSelector
+                {
+                    if (this._isPropertyValue())
+                        this.tokenType = "css-color";
+                    else if (this._condition.parseCondition === this._parseConditions.INITIAL)
+                        this.tokenType = "css-selector";
+                    else
+                        this.tokenType = null;
+                    return cursor;
+                }
+
+                <INITIAL> Variable
+                {
+                    if (this._condition.parseCondition === this._condition.parseCondition.INITIAL || this._condition.parseCondition === this._condition.parseCondition.AT_RULE)
+                        this._setParseCondition(this._parseConditions.PROPERTY);
+                    this.tokenType = "scss-variable";
+                    return cursor;
+                }
+
+                <INITIAL> Identifier
+                {
+                    var token = this._line.substring(cursorOnEnter, cursor);
+                    this.tokenType = null;
+                    if (this._condition.parseCondition === this._parseConditions.INITIAL || this._condition.parseCondition === this._parseConditions.PROPERTY) {
+                        if (token.charAt(0) === "@") {
+                            this.tokenType = "css-at-rule";
+                            this._setParseCondition(token === "@media" ? this._parseConditions.AT_MEDIA_RULE : this._parseConditions.AT_RULE);
+                            this._condition.atKeyword = token;
+                        } else if (this._condition.parseCondition === this._parseConditions.INITIAL)
+                            this.tokenType = "css-selector";
+                        else if (this._propertyKeywords.hasOwnProperty(token))
+                            this.tokenType = "css-property";
+                    } else if (this._condition.parseCondition === this._parseConditions.AT_MEDIA_RULE || this._condition.parseCondition === this._parseConditions.AT_RULE) {
+                        if (WebInspector.SourceCSSTokenizer.SCSSAtRelatedKeywords.hasOwnProperty(token))
+                            this.tokenType = "css-at-rule";
+                        else if (WebInspector.SourceCSSTokenizer.MediaTypes.hasOwnProperty(token))
+                            this.tokenType = "css-keyword";
+                    }
+                    if (this.tokenType)
+                        return cursor;
+
+                    if (this._isPropertyValue()) {
+                        var firstChar = token.charAt(0);
+                        if (firstChar === "$")
+                            this.tokenType = "scss-variable";
+                        else if (firstChar === "!")
+                            this.tokenType = "css-bang-keyword";
+                        else if (this._condition.atKeyword === "@extend")
+                            this.tokenType = "css-selector";
+                        else if (this._valueKeywords.hasOwnProperty(token) || this._scssValueKeywords.hasOwnProperty(token))
+                            this.tokenType = "css-keyword";
+                        else if (this._colorKeywords.hasOwnProperty(token)) {
+                            // FIXME: this does not convert tokens toLowerCase() for the sake of speed.
+                            this.tokenType = "css-color";
+                        }
+                    } else if (this._condition.parseCondition !== this._parseConditions.PROPERTY_VALUE)
+                        this.tokenType = "css-selector";
+                    return cursor;
+                }
+                <*> [^] { this.tokenType = null; return cursor; }
+            */
+        }
+    },
+
+    __proto__: WebInspector.SourceTokenizer.prototype
+}
diff --git a/Source/devtools/front_end/SourceFrame.js b/Source/devtools/front_end/SourceFrame.js
new file mode 100644
index 0000000..cd7beea
--- /dev/null
+++ b/Source/devtools/front_end/SourceFrame.js
@@ -0,0 +1,771 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @extends {WebInspector.View}
+ * @constructor
+ * @param {WebInspector.ContentProvider} contentProvider
+ */
+WebInspector.SourceFrame = function(contentProvider)
+{
+    WebInspector.View.call(this);
+    this.element.addStyleClass("script-view");
+    this.element.addStyleClass("fill");
+
+    this._url = contentProvider.contentURL();
+    this._contentProvider = contentProvider;
+
+    var textEditorDelegate = new WebInspector.TextEditorDelegateForSourceFrame(this);
+
+    if (WebInspector.settings.codemirror.get()) {
+        loadScript("CodeMirrorTextEditor.js");
+        this._textEditor = new WebInspector.CodeMirrorTextEditor(this._url, textEditorDelegate);
+    } else
+        this._textEditor = new WebInspector.DefaultTextEditor(this._url, textEditorDelegate);
+
+    this._currentSearchResultIndex = -1;
+    this._searchResults = [];
+
+    this._messages = [];
+    this._rowMessages = {};
+    this._messageBubbles = {};
+
+    this._textEditor.setReadOnly(!this.canEditSource());
+
+    this._shortcuts = {};
+    this._shortcuts[WebInspector.KeyboardShortcut.makeKey("s", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)] = this._commitEditing.bind(this);
+    this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false);
+
+    this._sourcePosition = new WebInspector.StatusBarText("", "source-frame-cursor-position");
+}
+
+/**
+ * @param {string} query
+ * @param {string=} modifiers
+ */
+WebInspector.SourceFrame.createSearchRegex = function(query, modifiers)
+{
+    var regex;
+    modifiers = modifiers || "";
+
+    // First try creating regex if user knows the / / hint.
+    try {
+        if (/^\/.*\/$/.test(query))
+            regex = new RegExp(query.substring(1, query.length - 1), modifiers);
+    } catch (e) {
+        // Silent catch.
+    }
+
+    // Otherwise just do case-insensitive search.
+    if (!regex)
+        regex = createPlainTextSearchRegex(query, "i" + modifiers);
+
+    return regex;
+}
+
+WebInspector.SourceFrame.Events = {
+    ScrollChanged: "ScrollChanged",
+    SelectionChanged: "SelectionChanged"
+}
+
+WebInspector.SourceFrame.prototype = {
+    wasShown: function()
+    {
+        this._ensureContentLoaded();
+        this._textEditor.show(this.element);
+        this._editorAttached = true;
+        this._wasShownOrLoaded();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _isEditorShowing: function()
+    {
+        return this.isShowing() && this._editorAttached;
+    },
+
+    willHide: function()
+    {
+        WebInspector.View.prototype.willHide.call(this);
+
+        this._clearLineHighlight();
+        this._clearLineToReveal();
+    },
+
+    /**
+     * @return {?Element}
+     */
+    statusBarText: function()
+    {
+        return this._sourcePosition.element;
+    },
+
+    /**
+     * @return {Array.<Element>}
+     */
+    statusBarItems: function()
+    {
+        return [];
+    },
+
+    defaultFocusedElement: function()
+    {
+        return this._textEditor.defaultFocusedElement();
+    },
+
+    get loaded()
+    {
+        return this._loaded;
+    },
+
+    hasContent: function()
+    {
+        return true;
+    },
+
+    get textEditor()
+    {
+        return this._textEditor;
+    },
+
+    _ensureContentLoaded: function()
+    {
+        if (!this._contentRequested) {
+            this._contentRequested = true;
+            this._contentProvider.requestContent(this.setContent.bind(this));
+        }
+    },
+
+    addMessage: function(msg)
+    {
+        this._messages.push(msg);
+        if (this.loaded)
+            this.addMessageToSource(msg.line - 1, msg);
+    },
+
+    clearMessages: function()
+    {
+        for (var line in this._messageBubbles) {
+            var bubble = this._messageBubbles[line];
+            var lineNumber = parseInt(line, 10);
+            this._textEditor.removeDecoration(lineNumber, bubble);
+        }
+
+        this._messages = [];
+        this._rowMessages = {};
+        this._messageBubbles = {};
+    },
+
+    /**
+     * @param {number} line
+     */
+    canHighlightLine: function(line)
+    {
+        return true;
+    },
+
+    /**
+     * @param {number} line
+     */
+    highlightLine: function(line)
+    {
+        this._clearLineToReveal();
+        this._clearLineToScrollTo();
+        this._lineToHighlight = line;
+        this._innerHighlightLineIfNeeded();
+        this._textEditor.setSelection(WebInspector.TextRange.createFromLocation(line, 0));
+    },
+
+    _innerHighlightLineIfNeeded: function()
+    {
+        if (typeof this._lineToHighlight === "number") {
+            if (this.loaded && this._isEditorShowing()) {
+                this._textEditor.highlightLine(this._lineToHighlight);
+                delete this._lineToHighlight
+            }
+        }
+    },
+
+    _clearLineHighlight: function()
+    {
+        this._textEditor.clearLineHighlight();
+        delete this._lineToHighlight;
+    },
+
+    /**
+     * @param {number} line
+     */
+    revealLine: function(line)
+    {
+        this._clearLineHighlight();
+        this._clearLineToScrollTo();
+        this._lineToReveal = line;
+        this._innerRevealLineIfNeeded();
+    },
+
+    _innerRevealLineIfNeeded: function()
+    {
+        if (typeof this._lineToReveal === "number") {
+            if (this.loaded && this._isEditorShowing()) {
+                this._textEditor.revealLine(this._lineToReveal);
+                delete this._lineToReveal
+            }
+        }
+    },
+
+    _clearLineToReveal: function()
+    {
+        delete this._lineToReveal;
+    },
+
+    /**
+     * @param {number} line
+     */
+    scrollToLine: function(line)
+    {
+        this._clearLineHighlight();
+        this._clearLineToReveal();
+        this._lineToScrollTo = line;
+        this._innerScrollToLineIfNeeded();
+    },
+
+    _innerScrollToLineIfNeeded: function()
+    {
+        if (typeof this._lineToScrollTo === "number") {
+            if (this.loaded && this._isEditorShowing()) {
+                this._textEditor.scrollToLine(this._lineToScrollTo);
+                delete this._lineToScrollTo;
+            }
+        }
+    },
+
+    _clearLineToScrollTo: function()
+    {
+        delete this._lineToScrollTo;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    setSelection: function(textRange)
+    {
+        this._selectionToSet = textRange;
+        this._innerSetSelectionIfNeeded();
+    },
+
+    _innerSetSelectionIfNeeded: function()
+    {
+        if (this._selectionToSet && this.loaded && this._isEditorShowing()) {
+            this._textEditor.setSelection(this._selectionToSet);
+            delete this._selectionToSet;
+        }
+    },
+
+    _wasShownOrLoaded: function()
+    {
+        this._innerHighlightLineIfNeeded();
+        this._innerRevealLineIfNeeded();
+        this._innerScrollToLineIfNeeded();
+        this._innerSetSelectionIfNeeded();
+    },
+
+    onTextChanged: function(oldRange, newRange)
+    {
+        if (!this._isReplacing)
+            WebInspector.searchController.cancelSearch();
+        this.clearMessages();
+    },
+
+    _simplifyMimeType: function(mimeType)
+    {
+        if (!mimeType)
+            return "";
+        if (mimeType.indexOf("javascript") >= 0 ||
+            mimeType.indexOf("jscript") >= 0 ||
+            mimeType.indexOf("ecmascript") >= 0)
+            return "text/javascript";
+        return mimeType;
+    },
+
+    /**
+     * @param {?string} content
+     * @param {boolean} contentEncoded
+     * @param {string} mimeType
+     */
+    setContent: function(content, contentEncoded, mimeType)
+    {
+        this._textEditor.mimeType = this._simplifyMimeType(mimeType);
+
+        if (!this._loaded) {
+            this._loaded = true;
+            this._textEditor.setText(content || "");
+            this._textEditor.markClean();
+        } else
+            this._textEditor.editRange(this._textEditor.range(), content || "");
+
+        this._textEditor.beginUpdates();
+
+        this._setTextEditorDecorations();
+
+        this._wasShownOrLoaded();
+
+        if (this._delayedFindSearchMatches) {
+            this._delayedFindSearchMatches();
+            delete this._delayedFindSearchMatches;
+        }
+
+        this.onTextEditorContentLoaded();
+
+        this._textEditor.endUpdates();
+    },
+
+    onTextEditorContentLoaded: function() {},
+
+    _setTextEditorDecorations: function()
+    {
+        this._rowMessages = {};
+        this._messageBubbles = {};
+
+        this._textEditor.beginUpdates();
+
+        this._addExistingMessagesToSource();
+
+        this._textEditor.endUpdates();
+    },
+
+    /**
+     * @param {string} query
+     * @param {function(WebInspector.View, number)} callback
+     */
+    performSearch: function(query, callback)
+    {
+        // Call searchCanceled since it will reset everything we need before doing a new search.
+        this.searchCanceled();
+
+        function doFindSearchMatches(query)
+        {
+            this._currentSearchResultIndex = -1;
+            this._searchResults = [];
+
+            var regex = WebInspector.SourceFrame.createSearchRegex(query);
+            this._searchResults = this._collectRegexMatches(regex);
+            var shiftToIndex = 0;
+            var selection = this._textEditor.lastSelection();
+            for (var i = 0; selection && i < this._searchResults.length; ++i) {
+                if (this._searchResults[i].compareTo(selection) >= 0) {
+                    shiftToIndex = i;
+                    break;
+                }
+            }
+
+            if (shiftToIndex)
+                this._searchResults = this._searchResults.rotate(shiftToIndex);
+
+            callback(this, this._searchResults.length);
+        }
+
+        if (this.loaded)
+            doFindSearchMatches.call(this, query);
+        else
+            this._delayedFindSearchMatches = doFindSearchMatches.bind(this, query);
+
+        this._ensureContentLoaded();
+    },
+
+    searchCanceled: function()
+    {
+        delete this._delayedFindSearchMatches;
+        if (!this.loaded)
+            return;
+
+        this._currentSearchResultIndex = -1;
+        this._searchResults = [];
+        this._textEditor.markAndRevealRange(null);
+    },
+
+    hasSearchResults: function()
+    {
+        return this._searchResults.length > 0;
+    },
+
+    jumpToFirstSearchResult: function()
+    {
+        this.jumpToSearchResult(0);
+    },
+
+    jumpToLastSearchResult: function()
+    {
+        this.jumpToSearchResult(this._searchResults.length - 1);
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        this.jumpToSearchResult(this._currentSearchResultIndex + 1);
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        this.jumpToSearchResult(this._currentSearchResultIndex - 1);
+    },
+
+    showingFirstSearchResult: function()
+    {
+        return this._searchResults.length &&  this._currentSearchResultIndex === 0;
+    },
+
+    showingLastSearchResult: function()
+    {
+        return this._searchResults.length && this._currentSearchResultIndex === (this._searchResults.length - 1);
+    },
+
+    get currentSearchResultIndex()
+    {
+        return this._currentSearchResultIndex;
+    },
+
+    jumpToSearchResult: function(index)
+    {
+        if (!this.loaded || !this._searchResults.length)
+            return;
+        this._currentSearchResultIndex = (index + this._searchResults.length) % this._searchResults.length;
+        this._textEditor.markAndRevealRange(this._searchResults[this._currentSearchResultIndex]);
+    },
+
+    /**
+     * @param {string} text
+     */
+    replaceSearchMatchWith: function(text)
+    {
+        var range = this._searchResults[this._currentSearchResultIndex];
+        if (!range)
+            return;
+        this._textEditor.markAndRevealRange(null);
+
+        this._isReplacing = true;
+        var newRange = this._textEditor.editRange(range, text);
+        delete this._isReplacing;
+
+        this._textEditor.setSelection(newRange.collapseToEnd());
+    },
+
+    /**
+     * @param {string} query
+     * @param {string} replacement
+     */
+    replaceAllWith: function(query, replacement)
+    {
+        this._textEditor.markAndRevealRange(null);
+
+        var text = this._textEditor.text();
+        var range = this._textEditor.range();
+        text = text.replace(WebInspector.SourceFrame.createSearchRegex(query, "g"), replacement);
+
+        this._isReplacing = true;
+        this._textEditor.editRange(range, text);
+        delete this._isReplacing;
+    },
+
+    _collectRegexMatches: function(regexObject)
+    {
+        var ranges = [];
+        for (var i = 0; i < this._textEditor.linesCount; ++i) {
+            var line = this._textEditor.line(i);
+            var offset = 0;
+            do {
+                var match = regexObject.exec(line);
+                if (match) {
+                    if (match[0].length)
+                        ranges.push(new WebInspector.TextRange(i, offset + match.index, i, offset + match.index + match[0].length));
+                    offset += match.index + 1;
+                    line = line.substring(match.index + 1);
+                }
+            } while (match && line);
+        }
+        return ranges;
+    },
+
+    _addExistingMessagesToSource: function()
+    {
+        var length = this._messages.length;
+        for (var i = 0; i < length; ++i)
+            this.addMessageToSource(this._messages[i].line - 1, this._messages[i]);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {WebInspector.ConsoleMessage} msg
+     */
+    addMessageToSource: function(lineNumber, msg)
+    {
+        if (lineNumber >= this._textEditor.linesCount)
+            lineNumber = this._textEditor.linesCount - 1;
+        if (lineNumber < 0)
+            lineNumber = 0;
+
+        var rowMessages = this._rowMessages[lineNumber];
+        if (!rowMessages) {
+            rowMessages = [];
+            this._rowMessages[lineNumber] = rowMessages;
+        }
+
+        for (var i = 0; i < rowMessages.length; ++i) {
+            if (rowMessages[i].consoleMessage.isEqual(msg)) {
+                rowMessages[i].repeatCount = msg.totalRepeatCount;
+                this._updateMessageRepeatCount(rowMessages[i]);
+                return;
+            }
+        }
+
+        var rowMessage = { consoleMessage: msg };
+        rowMessages.push(rowMessage);
+
+        this._textEditor.beginUpdates();
+        var messageBubbleElement = this._messageBubbles[lineNumber];
+        if (!messageBubbleElement) {
+            messageBubbleElement = document.createElement("div");
+            messageBubbleElement.className = "webkit-html-message-bubble";
+            this._messageBubbles[lineNumber] = messageBubbleElement;
+            this._textEditor.addDecoration(lineNumber, messageBubbleElement);
+        }
+
+        var imageElement = document.createElement("div");
+        switch (msg.level) {
+            case WebInspector.ConsoleMessage.MessageLevel.Error:
+                messageBubbleElement.addStyleClass("webkit-html-error-message");
+                imageElement.className = "error-icon-small";
+                break;
+            case WebInspector.ConsoleMessage.MessageLevel.Warning:
+                messageBubbleElement.addStyleClass("webkit-html-warning-message");
+                imageElement.className = "warning-icon-small";
+                break;
+        }
+
+        var messageLineElement = document.createElement("div");
+        messageLineElement.className = "webkit-html-message-line";
+        messageBubbleElement.appendChild(messageLineElement);
+
+        // Create the image element in the Inspector's document so we can use relative image URLs.
+        messageLineElement.appendChild(imageElement);
+        messageLineElement.appendChild(document.createTextNode(msg.message));
+
+        rowMessage.element = messageLineElement;
+        rowMessage.repeatCount = msg.totalRepeatCount;
+        this._updateMessageRepeatCount(rowMessage);
+        this._textEditor.endUpdates();
+    },
+
+    _updateMessageRepeatCount: function(rowMessage)
+    {
+        if (rowMessage.repeatCount < 2)
+            return;
+
+        if (!rowMessage.repeatCountElement) {
+            var repeatCountElement = document.createElement("span");
+            rowMessage.element.appendChild(repeatCountElement);
+            rowMessage.repeatCountElement = repeatCountElement;
+        }
+
+        rowMessage.repeatCountElement.textContent = WebInspector.UIString(" (repeated %d times)", rowMessage.repeatCount);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {WebInspector.ConsoleMessage} msg
+     */
+    removeMessageFromSource: function(lineNumber, msg)
+    {
+        if (lineNumber >= this._textEditor.linesCount)
+            lineNumber = this._textEditor.linesCount - 1;
+        if (lineNumber < 0)
+            lineNumber = 0;
+
+        var rowMessages = this._rowMessages[lineNumber];
+        for (var i = 0; rowMessages && i < rowMessages.length; ++i) {
+            var rowMessage = rowMessages[i];
+            if (rowMessage.consoleMessage !== msg)
+                continue;
+
+            var messageLineElement = rowMessage.element;
+            var messageBubbleElement = messageLineElement.parentElement;
+            messageBubbleElement.removeChild(messageLineElement);
+            rowMessages.remove(rowMessage);
+            if (!rowMessages.length)
+                delete this._rowMessages[lineNumber];
+            if (!messageBubbleElement.childElementCount) {
+                this._textEditor.removeDecoration(lineNumber, messageBubbleElement);
+                delete this._messageBubbles[lineNumber];
+            }
+            break;
+        }
+    },
+
+    populateLineGutterContextMenu: function(contextMenu, lineNumber)
+    {
+    },
+
+    populateTextAreaContextMenu: function(contextMenu, lineNumber)
+    {
+    },
+
+    inheritScrollPositions: function(sourceFrame)
+    {
+        this._textEditor.inheritScrollPositions(sourceFrame._textEditor);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canEditSource: function()
+    {
+        return false;
+    },
+
+    /**
+     * @param {string} text 
+     */
+    commitEditing: function(text)
+    {
+    },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    selectionChanged: function(textRange)
+    {
+        this._updateSourcePosition(textRange);
+        this.dispatchEventToListeners(WebInspector.SourceFrame.Events.SelectionChanged, textRange);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    _updateSourcePosition: function(textRange)
+    {
+        if (!textRange)
+            return;
+
+        if (textRange.isEmpty()) {
+            this._sourcePosition.setText(WebInspector.UIString("Line %d, Column %d", textRange.endLine + 1, textRange.endColumn + 1));
+            return;
+        }
+        textRange = textRange.normalize();
+
+        var selectedText = this._textEditor.copyRange(textRange);
+        if (textRange.startLine === textRange.endLine)
+            this._sourcePosition.setText(WebInspector.UIString("%d characters selected", selectedText.length));
+        else
+            this._sourcePosition.setText(WebInspector.UIString("%d lines, %d characters selected", textRange.endLine - textRange.startLine + 1, selectedText.length));
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollChanged: function(lineNumber)
+    {
+        this.dispatchEventToListeners(WebInspector.SourceFrame.Events.ScrollChanged, lineNumber);
+    },
+
+    _handleKeyDown: function(e)
+    {
+        var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(e);
+        var handler = this._shortcuts[shortcutKey];
+        if (handler && handler())
+            e.consume(true);
+    },
+
+    _commitEditing: function()
+    {
+        if (this._textEditor.readOnly())
+            return false;
+
+        var content = this._textEditor.text();
+        this.commitEditing(content);
+        return true;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+
+/**
+ * @implements {WebInspector.TextEditorDelegate}
+ * @constructor
+ */
+WebInspector.TextEditorDelegateForSourceFrame = function(sourceFrame)
+{
+    this._sourceFrame = sourceFrame;
+}
+
+WebInspector.TextEditorDelegateForSourceFrame.prototype = {
+    onTextChanged: function(oldRange, newRange)
+    {
+        this._sourceFrame.onTextChanged(oldRange, newRange);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    selectionChanged: function(textRange)
+    {
+        this._sourceFrame.selectionChanged(textRange);
+    },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollChanged: function(lineNumber)
+    {
+        this._sourceFrame.scrollChanged(lineNumber);
+    },
+
+    populateLineGutterContextMenu: function(contextMenu, lineNumber)
+    {
+        this._sourceFrame.populateLineGutterContextMenu(contextMenu, lineNumber);
+    },
+
+    populateTextAreaContextMenu: function(contextMenu, lineNumber)
+    {
+        this._sourceFrame.populateTextAreaContextMenu(contextMenu, lineNumber);
+    },
+
+    /**
+     * @param {string} hrefValue
+     * @param {boolean} isExternal
+     * @return {Element}
+     */
+    createLink: function(hrefValue, isExternal)
+    {
+        var targetLocation = WebInspector.ParsedURL.completeURL(this._sourceFrame._url, hrefValue);
+        return WebInspector.linkifyURLAsNode(targetLocation || hrefValue, hrefValue, undefined, isExternal);
+    },
+
+    __proto__: WebInspector.TextEditorDelegate.prototype
+}
diff --git a/Source/devtools/front_end/SourceHTMLTokenizer.js b/Source/devtools/front_end/SourceHTMLTokenizer.js
new file mode 100644
index 0000000..8120728
--- /dev/null
+++ b/Source/devtools/front_end/SourceHTMLTokenizer.js
@@ -0,0 +1,816 @@
+/* Generated by re2c 0.13.5 on Fri May  6 13:47:06 2011 */
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+// Generate js file as follows:
+//
+// re2c -isc devtools/front_end/SourceHTMLTokenizer.re2js \
+// | sed 's|^yy\([^:]*\)*\:|case \1:|' \
+// | sed 's|[*]cursor[+][+]|this._charAt(cursor++)|' \
+// | sed 's|[[*][+][+]cursor|this._charAt(++cursor)|' \
+// | sed 's|[*]cursor|this._charAt(cursor)|' \
+// | sed 's|yych = \*\([^;]*\)|yych = this._charAt\1|' \
+// | sed 's|{ gotoCase = \([^; continue; };]*\)|{ gotoCase = \1; continue; }|' \
+// | sed 's|unsigned\ int|var|' \
+// | sed 's|var\ yych|case 1: case 1: var yych|' > devtools/front_end/SourceHTMLTokenizer.js
+
+/**
+ * @constructor
+ * @extends {WebInspector.SourceTokenizer}
+ */
+WebInspector.SourceHTMLTokenizer = function()
+{
+    WebInspector.SourceTokenizer.call(this);
+
+    // The order is determined by the generated code.
+    this._lexConditions = {
+        INITIAL: 0,
+        COMMENT: 1,
+        DOCTYPE: 2,
+        TAG: 3,
+        DSTRING: 4,
+        SSTRING: 5
+    };
+    this.case_INITIAL = 1000;
+    this.case_COMMENT = 1001;
+    this.case_DOCTYPE = 1002;
+    this.case_TAG = 1003;
+    this.case_DSTRING = 1004;
+    this.case_SSTRING = 1005;
+
+    this._parseConditions = {
+        INITIAL: 0,
+        ATTRIBUTE: 1,
+        ATTRIBUTE_VALUE: 2,
+        LINKIFY: 4,
+        A_NODE: 8,
+        SCRIPT: 16,
+        STYLE: 32
+    };
+
+    this.condition = this.createInitialCondition();
+}
+
+WebInspector.SourceHTMLTokenizer.prototype = {
+    createInitialCondition: function()
+    {
+        return { lexCondition: this._lexConditions.INITIAL, parseCondition: this._parseConditions.INITIAL };
+    },
+
+    set line(line) {
+        if (this._condition.internalJavaScriptTokenizerCondition) {
+            var match = /<\/script/i.exec(line);
+            if (match) {
+                this._internalJavaScriptTokenizer.line = line.substring(0, match.index);
+            } else
+                this._internalJavaScriptTokenizer.line = line;
+        } else if (this._condition.internalCSSTokenizerCondition) {
+            var match = /<\/style/i.exec(line);
+            if (match) {
+                this._internalCSSTokenizer.line = line.substring(0, match.index);
+            } else
+                this._internalCSSTokenizer.line = line;
+        }
+        this._line = line;
+    },
+
+    _isExpectingAttribute: function()
+    {
+        return this._condition.parseCondition & this._parseConditions.ATTRIBUTE;
+    },
+
+    _isExpectingAttributeValue: function()
+    {
+        return this._condition.parseCondition & this._parseConditions.ATTRIBUTE_VALUE;
+    },
+
+    _setExpectingAttribute: function()
+    {
+        if (this._isExpectingAttributeValue())
+            this._condition.parseCondition ^= this._parseConditions.ATTRIBUTE_VALUE;
+        this._condition.parseCondition |= this._parseConditions.ATTRIBUTE;
+    },
+
+    _setExpectingAttributeValue: function()
+    {
+        if (this._isExpectingAttribute())
+            this._condition.parseCondition ^= this._parseConditions.ATTRIBUTE;
+        this._condition.parseCondition |= this._parseConditions.ATTRIBUTE_VALUE;
+    },
+
+    /**
+     * @param {boolean=} stringEnds
+     */
+    _stringToken: function(cursor, stringEnds)
+    {
+        if (!this._isExpectingAttributeValue()) {
+            this.tokenType = null;
+            return cursor;
+        }
+        this.tokenType = this._attrValueTokenType();
+        if (stringEnds)
+            this._setExpectingAttribute();
+        return cursor;
+    },
+
+    _attrValueTokenType: function()
+    {
+        if (this._condition.parseCondition & this._parseConditions.LINKIFY) {
+            if (this._condition.parseCondition & this._parseConditions.A_NODE)
+                return "html-external-link";
+            return "html-resource-link";
+        }
+        return "html-attribute-value";
+    },
+
+    get _internalJavaScriptTokenizer()
+    {
+        return WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/javascript");
+    },
+
+    get _internalCSSTokenizer()
+    {
+        return WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/css");
+    },
+
+    scriptStarted: function(cursor)
+    {
+        this._condition.internalJavaScriptTokenizerCondition = this._internalJavaScriptTokenizer.createInitialCondition();
+    },
+
+    scriptEnded: function(cursor)
+    {
+    },
+
+    styleSheetStarted: function(cursor)
+    {
+        this._condition.internalCSSTokenizerCondition = this._internalCSSTokenizer.createInitialCondition();
+    },
+
+    styleSheetEnded: function(cursor)
+    {
+    },
+
+    nextToken: function(cursor)
+    {
+        if (this._condition.internalJavaScriptTokenizerCondition) {
+            // Re-set line to force </script> detection first.
+            this.line = this._line;
+            if (cursor !== this._internalJavaScriptTokenizer._line.length) {
+                // Tokenizer is stateless, so restore its condition before tokenizing and save it after.
+                this._internalJavaScriptTokenizer.condition = this._condition.internalJavaScriptTokenizerCondition;
+                var result = this._internalJavaScriptTokenizer.nextToken(cursor);
+                this.tokenType = this._internalJavaScriptTokenizer.tokenType;
+                this._condition.internalJavaScriptTokenizerCondition = this._internalJavaScriptTokenizer.condition;
+                return result;
+            } else if (cursor !== this._line.length)
+                delete this._condition.internalJavaScriptTokenizerCondition;
+        } else if (this._condition.internalCSSTokenizerCondition) {
+            // Re-set line to force </style> detection first.
+            this.line = this._line;
+            if (cursor !== this._internalCSSTokenizer._line.length) {
+                // Tokenizer is stateless, so restore its condition before tokenizing and save it after.
+                this._internalCSSTokenizer.condition = this._condition.internalCSSTokenizerCondition;
+                var result = this._internalCSSTokenizer.nextToken(cursor);
+                this.tokenType = this._internalCSSTokenizer.tokenType;
+                this._condition.internalCSSTokenizerCondition = this._internalCSSTokenizer.condition;
+                return result;
+            } else if (cursor !== this._line.length)
+                delete this._condition.internalCSSTokenizerCondition;
+        }
+
+        var cursorOnEnter = cursor;
+        var gotoCase = 1;
+        var YYMARKER;
+        while (1) {
+            switch (gotoCase)
+            // Following comment is replaced with generated state machine.
+
+        {
+            case 1: var yych;
+            var yyaccept = 0;
+            if (this.getLexCondition() < 3) {
+                if (this.getLexCondition() < 1) {
+                    { gotoCase = this.case_INITIAL; continue; };
+                } else {
+                    if (this.getLexCondition() < 2) {
+                        { gotoCase = this.case_COMMENT; continue; };
+                    } else {
+                        { gotoCase = this.case_DOCTYPE; continue; };
+                    }
+                }
+            } else {
+                if (this.getLexCondition() < 4) {
+                    { gotoCase = this.case_TAG; continue; };
+                } else {
+                    if (this.getLexCondition() < 5) {
+                        { gotoCase = this.case_DSTRING; continue; };
+                    } else {
+                        { gotoCase = this.case_SSTRING; continue; };
+                    }
+                }
+            }
+/* *********************************** */
+case this.case_COMMENT:
+
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 4; continue; };
+                { gotoCase = 3; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 4; continue; };
+                if (yych == '-') { gotoCase = 6; continue; };
+                { gotoCase = 3; continue; };
+            }
+case 2:
+            { this.tokenType = "html-comment"; return cursor; }
+case 3:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 9; continue; };
+case 4:
+            ++cursor;
+case 5:
+            { this.tokenType = null; return cursor; }
+case 6:
+            yyaccept = 1;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych != '-') { gotoCase = 5; continue; };
+case 7:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '>') { gotoCase = 10; continue; };
+case 8:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 9:
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 2; continue; };
+                { gotoCase = 8; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 2; continue; };
+                if (yych == '-') { gotoCase = 12; continue; };
+                { gotoCase = 8; continue; };
+            }
+case 10:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.INITIAL);
+            { this.tokenType = "html-comment"; return cursor; }
+case 12:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '-') { gotoCase = 7; continue; };
+            cursor = YYMARKER;
+            if (yyaccept <= 0) {
+                { gotoCase = 2; continue; };
+            } else {
+                { gotoCase = 5; continue; };
+            }
+/* *********************************** */
+case this.case_DOCTYPE:
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 18; continue; };
+                { gotoCase = 17; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 18; continue; };
+                if (yych == '>') { gotoCase = 20; continue; };
+                { gotoCase = 17; continue; };
+            }
+case 16:
+            { this.tokenType = "html-doctype"; return cursor; }
+case 17:
+            yych = this._charAt(++cursor);
+            { gotoCase = 23; continue; };
+case 18:
+            ++cursor;
+            { this.tokenType = null; return cursor; }
+case 20:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.INITIAL);
+            { this.tokenType = "html-doctype"; return cursor; }
+case 22:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 23:
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 16; continue; };
+                { gotoCase = 22; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 16; continue; };
+                if (yych == '>') { gotoCase = 16; continue; };
+                { gotoCase = 22; continue; };
+            }
+/* *********************************** */
+case this.case_DSTRING:
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 28; continue; };
+                { gotoCase = 27; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 28; continue; };
+                if (yych == '"') { gotoCase = 30; continue; };
+                { gotoCase = 27; continue; };
+            }
+case 26:
+            { return this._stringToken(cursor); }
+case 27:
+            yych = this._charAt(++cursor);
+            { gotoCase = 34; continue; };
+case 28:
+            ++cursor;
+            { this.tokenType = null; return cursor; }
+case 30:
+            ++cursor;
+case 31:
+            this.setLexCondition(this._lexConditions.TAG);
+            { return this._stringToken(cursor, true); }
+case 32:
+            yych = this._charAt(++cursor);
+            { gotoCase = 31; continue; };
+case 33:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 34:
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 26; continue; };
+                { gotoCase = 33; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 26; continue; };
+                if (yych == '"') { gotoCase = 32; continue; };
+                { gotoCase = 33; continue; };
+            }
+/* *********************************** */
+case this.case_INITIAL:
+            yych = this._charAt(cursor);
+            if (yych == '<') { gotoCase = 39; continue; };
+            ++cursor;
+            { this.tokenType = null; return cursor; }
+case 39:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= '/') {
+                if (yych == '!') { gotoCase = 44; continue; };
+                if (yych >= '/') { gotoCase = 41; continue; };
+            } else {
+                if (yych <= 'S') {
+                    if (yych >= 'S') { gotoCase = 42; continue; };
+                } else {
+                    if (yych == 's') { gotoCase = 42; continue; };
+                }
+            }
+case 40:
+            this.setLexCondition(this._lexConditions.TAG);
+            {
+                    if (this._condition.parseCondition & (this._parseConditions.SCRIPT | this._parseConditions.STYLE)) {
+                        // Do not tokenize script and style tag contents, keep lexer state, even though processing "<".
+                        this.setLexCondition(this._lexConditions.INITIAL);
+                        this.tokenType = null;
+                        return cursor;
+                    }
+
+                    this._condition.parseCondition = this._parseConditions.INITIAL;
+                    this.tokenType = "html-tag";
+                    return cursor;
+                }
+case 41:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == 'S') { gotoCase = 73; continue; };
+            if (yych == 's') { gotoCase = 73; continue; };
+            { gotoCase = 40; continue; };
+case 42:
+            yych = this._charAt(++cursor);
+            if (yych <= 'T') {
+                if (yych == 'C') { gotoCase = 62; continue; };
+                if (yych >= 'T') { gotoCase = 63; continue; };
+            } else {
+                if (yych <= 'c') {
+                    if (yych >= 'c') { gotoCase = 62; continue; };
+                } else {
+                    if (yych == 't') { gotoCase = 63; continue; };
+                }
+            }
+case 43:
+            cursor = YYMARKER;
+            { gotoCase = 40; continue; };
+case 44:
+            yych = this._charAt(++cursor);
+            if (yych <= 'C') {
+                if (yych != '-') { gotoCase = 43; continue; };
+            } else {
+                if (yych <= 'D') { gotoCase = 46; continue; };
+                if (yych == 'd') { gotoCase = 46; continue; };
+                { gotoCase = 43; continue; };
+            }
+            yych = this._charAt(++cursor);
+            if (yych == '-') { gotoCase = 54; continue; };
+            { gotoCase = 43; continue; };
+case 46:
+            yych = this._charAt(++cursor);
+            if (yych == 'O') { gotoCase = 47; continue; };
+            if (yych != 'o') { gotoCase = 43; continue; };
+case 47:
+            yych = this._charAt(++cursor);
+            if (yych == 'C') { gotoCase = 48; continue; };
+            if (yych != 'c') { gotoCase = 43; continue; };
+case 48:
+            yych = this._charAt(++cursor);
+            if (yych == 'T') { gotoCase = 49; continue; };
+            if (yych != 't') { gotoCase = 43; continue; };
+case 49:
+            yych = this._charAt(++cursor);
+            if (yych == 'Y') { gotoCase = 50; continue; };
+            if (yych != 'y') { gotoCase = 43; continue; };
+case 50:
+            yych = this._charAt(++cursor);
+            if (yych == 'P') { gotoCase = 51; continue; };
+            if (yych != 'p') { gotoCase = 43; continue; };
+case 51:
+            yych = this._charAt(++cursor);
+            if (yych == 'E') { gotoCase = 52; continue; };
+            if (yych != 'e') { gotoCase = 43; continue; };
+case 52:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.DOCTYPE);
+            { this.tokenType = "html-doctype"; return cursor; }
+case 54:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 57; continue; };
+                { gotoCase = 54; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 57; continue; };
+                if (yych != '-') { gotoCase = 54; continue; };
+            }
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '-') { gotoCase = 59; continue; };
+            { gotoCase = 43; continue; };
+case 57:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.COMMENT);
+            { this.tokenType = "html-comment"; return cursor; }
+case 59:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych != '>') { gotoCase = 54; continue; };
+            ++cursor;
+            { this.tokenType = "html-comment"; return cursor; }
+case 62:
+            yych = this._charAt(++cursor);
+            if (yych == 'R') { gotoCase = 68; continue; };
+            if (yych == 'r') { gotoCase = 68; continue; };
+            { gotoCase = 43; continue; };
+case 63:
+            yych = this._charAt(++cursor);
+            if (yych == 'Y') { gotoCase = 64; continue; };
+            if (yych != 'y') { gotoCase = 43; continue; };
+case 64:
+            yych = this._charAt(++cursor);
+            if (yych == 'L') { gotoCase = 65; continue; };
+            if (yych != 'l') { gotoCase = 43; continue; };
+case 65:
+            yych = this._charAt(++cursor);
+            if (yych == 'E') { gotoCase = 66; continue; };
+            if (yych != 'e') { gotoCase = 43; continue; };
+case 66:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.TAG);
+            {
+                    if (this._condition.parseCondition & this._parseConditions.STYLE) {
+                        // Do not tokenize style tag contents, keep lexer state, even though processing "<".
+                        this.setLexCondition(this._lexConditions.INITIAL);
+                        this.tokenType = null;
+                        return cursor;
+                    }
+                    this.tokenType = "html-tag";
+                    this._condition.parseCondition = this._parseConditions.STYLE;
+                    this._setExpectingAttribute();
+                    return cursor;
+                }
+case 68:
+            yych = this._charAt(++cursor);
+            if (yych == 'I') { gotoCase = 69; continue; };
+            if (yych != 'i') { gotoCase = 43; continue; };
+case 69:
+            yych = this._charAt(++cursor);
+            if (yych == 'P') { gotoCase = 70; continue; };
+            if (yych != 'p') { gotoCase = 43; continue; };
+case 70:
+            yych = this._charAt(++cursor);
+            if (yych == 'T') { gotoCase = 71; continue; };
+            if (yych != 't') { gotoCase = 43; continue; };
+case 71:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.TAG);
+            {
+                    if (this._condition.parseCondition & this._parseConditions.SCRIPT) {
+                        // Do not tokenize script tag contents, keep lexer state, even though processing "<".
+                        this.setLexCondition(this._lexConditions.INITIAL);
+                        this.tokenType = null;
+                        return cursor;
+                    }
+                    this.tokenType = "html-tag";
+                    this._condition.parseCondition = this._parseConditions.SCRIPT;
+                    this._setExpectingAttribute();
+                    return cursor;
+                }
+case 73:
+            yych = this._charAt(++cursor);
+            if (yych <= 'T') {
+                if (yych == 'C') { gotoCase = 75; continue; };
+                if (yych <= 'S') { gotoCase = 43; continue; };
+            } else {
+                if (yych <= 'c') {
+                    if (yych <= 'b') { gotoCase = 43; continue; };
+                    { gotoCase = 75; continue; };
+                } else {
+                    if (yych != 't') { gotoCase = 43; continue; };
+                }
+            }
+            yych = this._charAt(++cursor);
+            if (yych == 'Y') { gotoCase = 81; continue; };
+            if (yych == 'y') { gotoCase = 81; continue; };
+            { gotoCase = 43; continue; };
+case 75:
+            yych = this._charAt(++cursor);
+            if (yych == 'R') { gotoCase = 76; continue; };
+            if (yych != 'r') { gotoCase = 43; continue; };
+case 76:
+            yych = this._charAt(++cursor);
+            if (yych == 'I') { gotoCase = 77; continue; };
+            if (yych != 'i') { gotoCase = 43; continue; };
+case 77:
+            yych = this._charAt(++cursor);
+            if (yych == 'P') { gotoCase = 78; continue; };
+            if (yych != 'p') { gotoCase = 43; continue; };
+case 78:
+            yych = this._charAt(++cursor);
+            if (yych == 'T') { gotoCase = 79; continue; };
+            if (yych != 't') { gotoCase = 43; continue; };
+case 79:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.TAG);
+            {
+                    this.tokenType = "html-tag";
+                    this._condition.parseCondition = this._parseConditions.INITIAL;
+                    this.scriptEnded(cursor - 8);
+                    return cursor;
+                }
+case 81:
+            yych = this._charAt(++cursor);
+            if (yych == 'L') { gotoCase = 82; continue; };
+            if (yych != 'l') { gotoCase = 43; continue; };
+case 82:
+            yych = this._charAt(++cursor);
+            if (yych == 'E') { gotoCase = 83; continue; };
+            if (yych != 'e') { gotoCase = 43; continue; };
+case 83:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.TAG);
+            {
+                    this.tokenType = "html-tag";
+                    this._condition.parseCondition = this._parseConditions.INITIAL;
+                    this.styleSheetEnded(cursor - 7);
+                    return cursor;
+                }
+/* *********************************** */
+case this.case_SSTRING:
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 89; continue; };
+                { gotoCase = 88; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 89; continue; };
+                if (yych == '\'') { gotoCase = 91; continue; };
+                { gotoCase = 88; continue; };
+            }
+case 87:
+            { return this._stringToken(cursor); }
+case 88:
+            yych = this._charAt(++cursor);
+            { gotoCase = 95; continue; };
+case 89:
+            ++cursor;
+            { this.tokenType = null; return cursor; }
+case 91:
+            ++cursor;
+case 92:
+            this.setLexCondition(this._lexConditions.TAG);
+            { return this._stringToken(cursor, true); }
+case 93:
+            yych = this._charAt(++cursor);
+            { gotoCase = 92; continue; };
+case 94:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 95:
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 87; continue; };
+                { gotoCase = 94; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 87; continue; };
+                if (yych == '\'') { gotoCase = 93; continue; };
+                { gotoCase = 94; continue; };
+            }
+/* *********************************** */
+case this.case_TAG:
+            yych = this._charAt(cursor);
+            if (yych <= '&') {
+                if (yych <= '\r') {
+                    if (yych == '\n') { gotoCase = 100; continue; };
+                    if (yych >= '\r') { gotoCase = 100; continue; };
+                } else {
+                    if (yych <= ' ') {
+                        if (yych >= ' ') { gotoCase = 100; continue; };
+                    } else {
+                        if (yych == '"') { gotoCase = 102; continue; };
+                    }
+                }
+            } else {
+                if (yych <= '>') {
+                    if (yych <= ';') {
+                        if (yych <= '\'') { gotoCase = 103; continue; };
+                    } else {
+                        if (yych <= '<') { gotoCase = 100; continue; };
+                        if (yych <= '=') { gotoCase = 104; continue; };
+                        { gotoCase = 106; continue; };
+                    }
+                } else {
+                    if (yych <= '[') {
+                        if (yych >= '[') { gotoCase = 100; continue; };
+                    } else {
+                        if (yych == ']') { gotoCase = 100; continue; };
+                    }
+                }
+            }
+            ++cursor;
+            yych = this._charAt(cursor);
+            { gotoCase = 119; continue; };
+case 99:
+            {
+                    if (this._condition.parseCondition === this._parseConditions.SCRIPT || this._condition.parseCondition === this._parseConditions.STYLE) {
+                        // Fall through if expecting attributes.
+                        this.tokenType = null;
+                        return cursor;
+                    }
+
+                    if (this._condition.parseCondition === this._parseConditions.INITIAL) {
+                        this.tokenType = "html-tag";
+                        this._setExpectingAttribute();
+                        var token = this._line.substring(cursorOnEnter, cursor);
+                        if (token === "a")
+                            this._condition.parseCondition |= this._parseConditions.A_NODE;
+                        else if (this._condition.parseCondition & this._parseConditions.A_NODE)
+                            this._condition.parseCondition ^= this._parseConditions.A_NODE;
+                    } else if (this._isExpectingAttribute()) {
+                        var token = this._line.substring(cursorOnEnter, cursor);
+                        if (token === "href" || token === "src")
+                            this._condition.parseCondition |= this._parseConditions.LINKIFY;
+                        else if (this._condition.parseCondition |= this._parseConditions.LINKIFY)
+                            this._condition.parseCondition ^= this._parseConditions.LINKIFY;
+                        this.tokenType = "html-attribute-name";
+                    } else if (this._isExpectingAttributeValue())
+                        this.tokenType = this._attrValueTokenType();
+                    else
+                        this.tokenType = null;
+                    return cursor;
+                }
+case 100:
+            ++cursor;
+            { this.tokenType = null; return cursor; }
+case 102:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 115; continue; };
+case 103:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 109; continue; };
+case 104:
+            ++cursor;
+            {
+                    if (this._isExpectingAttribute())
+                        this._setExpectingAttributeValue();
+                    this.tokenType = null;
+                    return cursor;
+                }
+case 106:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.INITIAL);
+            {
+                    this.tokenType = "html-tag";
+                    if (this._condition.parseCondition & this._parseConditions.SCRIPT) {
+                        this.scriptStarted(cursor);
+                        // Do not tokenize script tag contents.
+                        return cursor;
+                    }
+
+                    if (this._condition.parseCondition & this._parseConditions.STYLE) {
+                        this.styleSheetStarted(cursor);
+                        // Do not tokenize style tag contents.
+                        return cursor;
+                    }
+
+                    this._condition.parseCondition = this._parseConditions.INITIAL;
+                    return cursor;
+                }
+case 108:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 109:
+            if (yych <= '\f') {
+                if (yych != '\n') { gotoCase = 108; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 110; continue; };
+                if (yych == '\'') { gotoCase = 112; continue; };
+                { gotoCase = 108; continue; };
+            }
+case 110:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.SSTRING);
+            { return this._stringToken(cursor); }
+case 112:
+            ++cursor;
+            { return this._stringToken(cursor, true); }
+case 114:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 115:
+            if (yych <= '\f') {
+                if (yych != '\n') { gotoCase = 114; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 116; continue; };
+                if (yych == '"') { gotoCase = 112; continue; };
+                { gotoCase = 114; continue; };
+            }
+case 116:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.DSTRING);
+            { return this._stringToken(cursor); }
+case 118:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 119:
+            if (yych <= '"') {
+                if (yych <= '\r') {
+                    if (yych == '\n') { gotoCase = 99; continue; };
+                    if (yych <= '\f') { gotoCase = 118; continue; };
+                    { gotoCase = 99; continue; };
+                } else {
+                    if (yych == ' ') { gotoCase = 99; continue; };
+                    if (yych <= '!') { gotoCase = 118; continue; };
+                    { gotoCase = 99; continue; };
+                }
+            } else {
+                if (yych <= '>') {
+                    if (yych == '\'') { gotoCase = 99; continue; };
+                    if (yych <= ';') { gotoCase = 118; continue; };
+                    { gotoCase = 99; continue; };
+                } else {
+                    if (yych <= '[') {
+                        if (yych <= 'Z') { gotoCase = 118; continue; };
+                        { gotoCase = 99; continue; };
+                    } else {
+                        if (yych == ']') { gotoCase = 99; continue; };
+                        { gotoCase = 118; continue; };
+                    }
+                }
+            }
+        }
+
+        }
+    },
+
+    __proto__: WebInspector.SourceTokenizer.prototype
+}
diff --git a/Source/devtools/front_end/SourceHTMLTokenizer.re2js b/Source/devtools/front_end/SourceHTMLTokenizer.re2js
new file mode 100644
index 0000000..971fff8
--- /dev/null
+++ b/Source/devtools/front_end/SourceHTMLTokenizer.re2js
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+// Generate js file as follows:
+//
+// re2c -isc devtools/front_end/SourceHTMLTokenizer.re2js \
+// | sed 's|^yy\([^:]*\)*\:|case \1:|' \
+// | sed 's|[*]cursor[+][+]|this._charAt(cursor++)|' \
+// | sed 's|[[*][+][+]cursor|this._charAt(++cursor)|' \
+// | sed 's|[*]cursor|this._charAt(cursor)|' \
+// | sed 's|yych = \*\([^;]*\)|yych = this._charAt\1|' \
+// | sed 's|goto case \([^;]*\)|{ gotoCase = \1; continue; }|' \
+// | sed 's|unsigned\ int|var|' \
+// | sed 's|var\ yych|case 1: var yych|' > devtools/front_end/SourceHTMLTokenizer.js
+
+/**
+ * @constructor
+ * @extends {WebInspector.SourceTokenizer}
+ */
+WebInspector.SourceHTMLTokenizer = function()
+{
+    WebInspector.SourceTokenizer.call(this);
+
+    // The order is determined by the generated code.
+    this._lexConditions = {
+        INITIAL: 0,
+        COMMENT: 1,
+        DOCTYPE: 2,
+        TAG: 3,
+        DSTRING: 4,
+        SSTRING: 5
+    };
+    this.case_INITIAL = 1000;
+    this.case_COMMENT = 1001;
+    this.case_DOCTYPE = 1002;
+    this.case_TAG = 1003;
+    this.case_DSTRING = 1004;
+    this.case_SSTRING = 1005;
+
+    this._parseConditions = {
+        INITIAL: 0,
+        ATTRIBUTE: 1,
+        ATTRIBUTE_VALUE: 2,
+        LINKIFY: 4,
+        A_NODE: 8,
+        SCRIPT: 16,
+        STYLE: 32
+    };
+
+    this.condition = this.createInitialCondition();
+}
+
+WebInspector.SourceHTMLTokenizer.prototype = {
+    createInitialCondition: function()
+    {
+        return { lexCondition: this._lexConditions.INITIAL, parseCondition: this._parseConditions.INITIAL };
+    },
+
+    set line(line) {
+        if (this._condition.internalJavaScriptTokenizerCondition) {
+            var match = /<\/script/i.exec(line);
+            if (match) {
+                this._internalJavaScriptTokenizer.line = line.substring(0, match.index);
+            } else
+                this._internalJavaScriptTokenizer.line = line;
+        } else if (this._condition.internalCSSTokenizerCondition) {
+            var match = /<\/style/i.exec(line);
+            if (match) {
+                this._internalCSSTokenizer.line = line.substring(0, match.index);
+            } else
+                this._internalCSSTokenizer.line = line;
+        }
+        this._line = line;
+    },
+
+    _isExpectingAttribute: function()
+    {
+        return this._condition.parseCondition & this._parseConditions.ATTRIBUTE;
+    },
+
+    _isExpectingAttributeValue: function()
+    {
+        return this._condition.parseCondition & this._parseConditions.ATTRIBUTE_VALUE;
+    },
+
+    _setExpectingAttribute: function()
+    {
+        if (this._isExpectingAttributeValue())
+            this._condition.parseCondition ^= this._parseConditions.ATTRIBUTE_VALUE;
+        this._condition.parseCondition |= this._parseConditions.ATTRIBUTE;
+    },
+
+    _setExpectingAttributeValue: function()
+    {
+        if (this._isExpectingAttribute())
+            this._condition.parseCondition ^= this._parseConditions.ATTRIBUTE;
+        this._condition.parseCondition |= this._parseConditions.ATTRIBUTE_VALUE;
+    },
+
+    /**
+     * @param {boolean=} stringEnds
+     */
+    _stringToken: function(cursor, stringEnds)
+    {
+        if (!this._isExpectingAttributeValue()) {
+            this.tokenType = null;
+            return cursor;
+        }
+        this.tokenType = this._attrValueTokenType();
+        if (stringEnds)
+            this._setExpectingAttribute();
+        return cursor;
+    },
+
+    _attrValueTokenType: function()
+    {
+        if (this._condition.parseCondition & this._parseConditions.LINKIFY) {
+            if (this._condition.parseCondition & this._parseConditions.A_NODE)
+                return "html-external-link";
+            return "html-resource-link";
+        }
+        return "html-attribute-value";
+    },
+
+    get _internalJavaScriptTokenizer()
+    {
+        return WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/javascript");
+    },
+
+    get _internalCSSTokenizer()
+    {
+        return WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/css");
+    },
+
+    scriptStarted: function(cursor)
+    {
+        this._condition.internalJavaScriptTokenizerCondition = this._internalJavaScriptTokenizer.createInitialCondition();
+    },
+
+    scriptEnded: function(cursor)
+    {
+    },
+
+    styleSheetStarted: function(cursor)
+    {
+        this._condition.internalCSSTokenizerCondition = this._internalCSSTokenizer.createInitialCondition();
+    },
+
+    styleSheetEnded: function(cursor)
+    {
+    },
+
+    nextToken: function(cursor)
+    {
+        if (this._condition.internalJavaScriptTokenizerCondition) {
+            // Re-set line to force </script> detection first.
+            this.line = this._line;
+            if (cursor !== this._internalJavaScriptTokenizer._line.length) {
+                // Tokenizer is stateless, so restore its condition before tokenizing and save it after.
+                this._internalJavaScriptTokenizer.condition = this._condition.internalJavaScriptTokenizerCondition;
+                var result = this._internalJavaScriptTokenizer.nextToken(cursor);
+                this.tokenType = this._internalJavaScriptTokenizer.tokenType;
+                this._condition.internalJavaScriptTokenizerCondition = this._internalJavaScriptTokenizer.condition;
+                return result;
+            } else if (cursor !== this._line.length)
+                delete this._condition.internalJavaScriptTokenizerCondition;
+        } else if (this._condition.internalCSSTokenizerCondition) {
+            // Re-set line to force </style> detection first.
+            this.line = this._line;
+            if (cursor !== this._internalCSSTokenizer._line.length) {
+                // Tokenizer is stateless, so restore its condition before tokenizing and save it after.
+                this._internalCSSTokenizer.condition = this._condition.internalCSSTokenizerCondition;
+                var result = this._internalCSSTokenizer.nextToken(cursor);
+                this.tokenType = this._internalCSSTokenizer.tokenType;
+                this._condition.internalCSSTokenizerCondition = this._internalCSSTokenizer.condition;
+                return result;
+            } else if (cursor !== this._line.length)
+                delete this._condition.internalCSSTokenizerCondition;
+        }
+
+        var cursorOnEnter = cursor;
+        var gotoCase = 1;
+        var YYMARKER;
+        while (1) {
+            switch (gotoCase)
+            // Following comment is replaced with generated state machine.
+            /*!re2c
+                re2c:define:YYCTYPE  = "var";
+                re2c:define:YYCURSOR = cursor;
+                re2c:define:YYGETCONDITION = "this.getLexCondition";
+                re2c:define:YYSETCONDITION = "this.setLexCondition";
+                re2c:condprefix = "case this.case_";
+                re2c:condenumprefix = "this._lexConditions.";
+                re2c:yyfill:enable = 0;
+                re2c:labelprefix = "case ";
+                re2c:indent:top = 2;
+                re2c:indent:string = "    ";
+
+                CommentContent = ([^-\r\n] | ("--" [^>]))*;
+                Comment = "<!--" CommentContent "-->";
+                CommentStart = "<!--" CommentContent [\r\n];
+                CommentEnd = CommentContent "-->";
+
+                DocTypeStart = "<!" [Dd] [Oo] [Cc] [Tt] [Yy] [Pp] [Ee];
+                DocTypeContent = [^\r\n>]*;
+
+                ScriptStart = "<" [Ss] [Cc] [Rr] [Ii] [Pp] [Tt];
+                ScriptEnd = "</" [Ss] [Cc] [Rr] [Ii] [Pp] [Tt];
+
+                StyleStart = "<" [Ss] [Tt] [Yy] [Ll] [Ee];
+                StyleEnd = "</" [Ss] [Tt] [Yy] [Ll] [Ee];
+
+                LT = "<" | "</";
+                GT = ">";
+                EqualSign = "=";
+
+                DoubleStringContent = [^\r\n\"]*;
+                SingleStringContent = [^\r\n\']*;
+                StringLiteral = "\"" DoubleStringContent "\"" | "'" SingleStringContent "'";
+                DoubleStringStart = "\"" DoubleStringContent [\r\n];
+                DoubleStringEnd = DoubleStringContent "\"";
+                SingleStringStart = "'" SingleStringContent [\r\n];
+                SingleStringEnd = SingleStringContent "'";
+
+                Identifier = [^ \r\n"'<>\[\]=]+;
+
+                <INITIAL> Comment { this.tokenType = "html-comment"; return cursor; }
+                <INITIAL> CommentStart => COMMENT { this.tokenType = "html-comment"; return cursor; }
+                <COMMENT> CommentContent => COMMENT { this.tokenType = "html-comment"; return cursor; }
+                <COMMENT> CommentEnd => INITIAL { this.tokenType = "html-comment"; return cursor; }
+
+                <INITIAL> DocTypeStart => DOCTYPE { this.tokenType = "html-doctype"; return cursor; }
+                <DOCTYPE> DocTypeContent => DOCTYPE { this.tokenType = "html-doctype"; return cursor; }
+                <DOCTYPE> GT => INITIAL { this.tokenType = "html-doctype"; return cursor; }
+
+                <INITIAL> ScriptStart => TAG
+                {
+                    if (this._condition.parseCondition & this._parseConditions.SCRIPT) {
+                        // Do not tokenize script tag contents, keep lexer state, even though processing "<".
+                        this.setLexCondition(this._lexConditions.INITIAL);
+                        this.tokenType = null;
+                        return cursor;
+                    }
+                    this.tokenType = "html-tag";
+                    this._condition.parseCondition = this._parseConditions.SCRIPT;
+                    this._setExpectingAttribute();
+                    return cursor;
+                }
+
+                <INITIAL> ScriptEnd => TAG
+                {
+                    this.tokenType = "html-tag";
+                    this._condition.parseCondition = this._parseConditions.INITIAL;
+                    this.scriptEnded(cursor - 8);
+                    return cursor;
+                }
+
+                <INITIAL> StyleStart => TAG
+                {
+                    if (this._condition.parseCondition & this._parseConditions.STYLE) {
+                        // Do not tokenize style tag contents, keep lexer state, even though processing "<".
+                        this.setLexCondition(this._lexConditions.INITIAL);
+                        this.tokenType = null;
+                        return cursor;
+                    }
+                    this.tokenType = "html-tag";
+                    this._condition.parseCondition = this._parseConditions.STYLE;
+                    this._setExpectingAttribute();
+                    return cursor;
+                }
+
+                <INITIAL> StyleEnd => TAG
+                {
+                    this.tokenType = "html-tag";
+                    this._condition.parseCondition = this._parseConditions.INITIAL;
+                    this.styleSheetEnded(cursor - 7);
+                    return cursor;
+                }
+
+                <INITIAL> LT => TAG
+                {
+                    if (this._condition.parseCondition & (this._parseConditions.SCRIPT | this._parseConditions.STYLE)) {
+                        // Do not tokenize script and style tag contents, keep lexer state, even though processing "<".
+                        this.setLexCondition(this._lexConditions.INITIAL);
+                        this.tokenType = null;
+                        return cursor;
+                    }
+
+                    this._condition.parseCondition = this._parseConditions.INITIAL;
+                    this.tokenType = "html-tag";
+                    return cursor;
+                }
+
+                <TAG> GT => INITIAL
+                {
+                    this.tokenType = "html-tag";
+                    if (this._condition.parseCondition & this._parseConditions.SCRIPT) {
+                        this.scriptStarted(cursor);
+                        // Do not tokenize script tag contents.
+                        return cursor;
+                    }
+
+                    if (this._condition.parseCondition & this._parseConditions.STYLE) {
+                        this.styleSheetStarted(cursor);
+                        // Do not tokenize style tag contents.
+                        return cursor;
+                    }
+
+                    this._condition.parseCondition = this._parseConditions.INITIAL;
+                    return cursor;
+                }
+
+                <TAG> StringLiteral { return this._stringToken(cursor, true); }
+                <TAG> DoubleStringStart => DSTRING { return this._stringToken(cursor); }
+                <DSTRING> DoubleStringContent => DSTRING { return this._stringToken(cursor); }
+                <DSTRING> DoubleStringEnd => TAG { return this._stringToken(cursor, true); }
+                <TAG> SingleStringStart => SSTRING { return this._stringToken(cursor); }
+                <SSTRING> SingleStringContent => SSTRING { return this._stringToken(cursor); }
+                <SSTRING> SingleStringEnd => TAG { return this._stringToken(cursor, true); }
+
+                <TAG> EqualSign => TAG
+                {
+                    if (this._isExpectingAttribute())
+                        this._setExpectingAttributeValue();
+                    this.tokenType = null;
+                    return cursor;
+                }
+
+                <TAG> Identifier
+                {
+                    if (this._condition.parseCondition === this._parseConditions.SCRIPT || this._condition.parseCondition === this._parseConditions.STYLE) {
+                        // Fall through if expecting attributes.
+                        this.tokenType = null;
+                        return cursor;
+                    }
+
+                    if (this._condition.parseCondition === this._parseConditions.INITIAL) {
+                        this.tokenType = "html-tag";
+                        this._setExpectingAttribute();
+                        var token = this._line.substring(cursorOnEnter, cursor);
+                        if (token === "a")
+                            this._condition.parseCondition |= this._parseConditions.A_NODE;
+                        else if (this._condition.parseCondition & this._parseConditions.A_NODE)
+                            this._condition.parseCondition ^= this._parseConditions.A_NODE;
+                    } else if (this._isExpectingAttribute()) {
+                        var token = this._line.substring(cursorOnEnter, cursor);
+                        if (token === "href" || token === "src")
+                            this._condition.parseCondition |= this._parseConditions.LINKIFY;
+                        else if (this._condition.parseCondition |= this._parseConditions.LINKIFY)
+                            this._condition.parseCondition ^= this._parseConditions.LINKIFY;
+                        this.tokenType = "html-attribute-name";
+                    } else if (this._isExpectingAttributeValue())
+                        this.tokenType = this._attrValueTokenType();
+                    else
+                        this.tokenType = null;
+                    return cursor;
+                }
+                <*> [^] { this.tokenType = null; return cursor; }
+            */
+        }
+    },
+
+    __proto__: WebInspector.SourceTokenizer.prototype
+}
diff --git a/Source/devtools/front_end/SourceJavaScriptTokenizer.js b/Source/devtools/front_end/SourceJavaScriptTokenizer.js
new file mode 100644
index 0000000..189cdf5
--- /dev/null
+++ b/Source/devtools/front_end/SourceJavaScriptTokenizer.js
@@ -0,0 +1,2490 @@
+/* Generated by re2c 0.13.5 on Tue Feb 19 16:16:47 2013 */
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+/*
+ * Generate js file as follows:
+re2c -isc devtools/front_end/SourceJavaScriptTokenizer.re2js \
+  | sed 's|^yy\([^:]*\)*\:|case \1:|' \
+  | sed 's|[*]cursor[+][+]|this._charAt(cursor++)|' \
+  | sed 's|[[*][+][+]cursor|this._charAt(++cursor)|' \
+  | sed 's|[*]cursor|this._charAt(cursor)|' \
+  | sed 's|yych = \*\([^;]*\)|yych = this._charAt\1|' \
+  | sed 's|{ gotoCase = \([^; continue; };]*\)|{ gotoCase = \1; continue; }|' \
+  | sed 's|yych <= \(0x[0-9a-fA-F]*\)|yych \<\= String.fromCharCode(\1)|' \
+  | sed 's|unsigned\ int|var|' \
+  | sed 's|var\ yych|case 1: case 1: var yych|' > devtools/front_end/SourceJavaScriptTokenizer.js
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SourceTokenizer}
+ */
+WebInspector.SourceJavaScriptTokenizer = function()
+{
+    WebInspector.SourceTokenizer.call(this);
+
+    this._lexConditions = {
+        DIV: 0,
+        NODIV: 1,
+        COMMENT: 2,
+        DSTRING: 3,
+        SSTRING: 4,
+        REGEX: 5
+    };
+
+    this.case_DIV = 1000;
+    this.case_NODIV = 1001;
+    this.case_COMMENT = 1002;
+    this.case_DSTRING = 1003;
+    this.case_SSTRING = 1004;
+    this.case_REGEX = 1005;
+
+    this.condition = this.createInitialCondition();
+}
+
+WebInspector.SourceJavaScriptTokenizer.Keywords = [
+        "null", "true", "false", "break", "case", "catch", "const", "default", "finally", "for",
+        "instanceof", "new", "var", "continue", "function", "return", "void", "delete", "if",
+        "this", "do", "while", "else", "in", "switch", "throw", "try", "typeof", "debugger",
+        "class", "enum", "export", "extends", "import", "super", "get", "set", "with"
+    ].keySet();
+
+WebInspector.SourceJavaScriptTokenizer.GlobalObjectValueProperties = {
+    "NaN": "javascript-nan",
+    "undefined": "javascript-undef",
+    "Infinity": "javascript-inf"
+};
+
+WebInspector.SourceJavaScriptTokenizer.prototype = {
+    createInitialCondition: function()
+    {
+        return { lexCondition: this._lexConditions.NODIV };
+    },
+
+    nextToken: function(cursor)
+    {
+        var cursorOnEnter = cursor;
+        var gotoCase = 1;
+        var YYMARKER;
+        while (1) {
+            switch (gotoCase)
+            // Following comment is replaced with generated state machine.
+            
+        {
+            case 1: var yych;
+            var yyaccept = 0;
+            if (this.getLexCondition() < 3) {
+                if (this.getLexCondition() < 1) {
+                    { gotoCase = this.case_DIV; continue; };
+                } else {
+                    if (this.getLexCondition() < 2) {
+                        { gotoCase = this.case_NODIV; continue; };
+                    } else {
+                        { gotoCase = this.case_COMMENT; continue; };
+                    }
+                }
+            } else {
+                if (this.getLexCondition() < 4) {
+                    { gotoCase = this.case_DSTRING; continue; };
+                } else {
+                    if (this.getLexCondition() < 5) {
+                        { gotoCase = this.case_SSTRING; continue; };
+                    } else {
+                        { gotoCase = this.case_REGEX; continue; };
+                    }
+                }
+            }
+/* *********************************** */
+case this.case_COMMENT:
+
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 4; continue; };
+                { gotoCase = 3; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 4; continue; };
+                if (yych == '*') { gotoCase = 6; continue; };
+                { gotoCase = 3; continue; };
+            }
+case 2:
+            { this.tokenType = "javascript-comment"; return cursor; }
+case 3:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 12; continue; };
+case 4:
+            ++cursor;
+            { this.tokenType = null; return cursor; }
+case 6:
+            yyaccept = 1;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == '*') { gotoCase = 9; continue; };
+            if (yych != '/') { gotoCase = 11; continue; };
+case 7:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.NODIV);
+            { this.tokenType = "javascript-comment"; return cursor; }
+case 9:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '*') { gotoCase = 9; continue; };
+            if (yych == '/') { gotoCase = 7; continue; };
+case 11:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 12:
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 2; continue; };
+                { gotoCase = 11; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 2; continue; };
+                if (yych == '*') { gotoCase = 9; continue; };
+                { gotoCase = 11; continue; };
+            }
+/* *********************************** */
+case this.case_DIV:
+            yych = this._charAt(cursor);
+            if (yych <= '9') {
+                if (yych <= '\'') {
+                    if (yych <= '"') {
+                        if (yych <= String.fromCharCode(0x1F)) { gotoCase = 15; continue; };
+                        if (yych <= ' ') { gotoCase = 17; continue; };
+                        if (yych <= '!') { gotoCase = 19; continue; };
+                        { gotoCase = 21; continue; };
+                    } else {
+                        if (yych <= '$') {
+                            if (yych >= '$') { gotoCase = 22; continue; };
+                        } else {
+                            if (yych <= '%') { gotoCase = 24; continue; };
+                            if (yych <= '&') { gotoCase = 25; continue; };
+                            { gotoCase = 26; continue; };
+                        }
+                    }
+                } else {
+                    if (yych <= ',') {
+                        if (yych <= ')') {
+                            if (yych <= '(') { gotoCase = 27; continue; };
+                            { gotoCase = 28; continue; };
+                        } else {
+                            if (yych <= '*') { gotoCase = 30; continue; };
+                            if (yych <= '+') { gotoCase = 31; continue; };
+                            { gotoCase = 27; continue; };
+                        }
+                    } else {
+                        if (yych <= '.') {
+                            if (yych <= '-') { gotoCase = 32; continue; };
+                            { gotoCase = 33; continue; };
+                        } else {
+                            if (yych <= '/') { gotoCase = 34; continue; };
+                            if (yych <= '0') { gotoCase = 36; continue; };
+                            { gotoCase = 38; continue; };
+                        }
+                    }
+                }
+            } else {
+                if (yych <= '\\') {
+                    if (yych <= '>') {
+                        if (yych <= ';') { gotoCase = 27; continue; };
+                        if (yych <= '<') { gotoCase = 39; continue; };
+                        if (yych <= '=') { gotoCase = 40; continue; };
+                        { gotoCase = 41; continue; };
+                    } else {
+                        if (yych <= '@') {
+                            if (yych <= '?') { gotoCase = 27; continue; };
+                        } else {
+                            if (yych <= 'Z') { gotoCase = 22; continue; };
+                            if (yych <= '[') { gotoCase = 27; continue; };
+                            { gotoCase = 42; continue; };
+                        }
+                    }
+                } else {
+                    if (yych <= 'z') {
+                        if (yych <= '^') {
+                            if (yych <= ']') { gotoCase = 27; continue; };
+                            { gotoCase = 43; continue; };
+                        } else {
+                            if (yych != '`') { gotoCase = 22; continue; };
+                        }
+                    } else {
+                        if (yych <= '|') {
+                            if (yych <= '{') { gotoCase = 27; continue; };
+                            { gotoCase = 44; continue; };
+                        } else {
+                            if (yych <= '~') { gotoCase = 27; continue; };
+                            if (yych >= 0x80) { gotoCase = 22; continue; };
+                        }
+                    }
+                }
+            }
+case 15:
+            ++cursor;
+case 16:
+            { this.tokenType = null; return cursor; }
+case 17:
+            ++cursor;
+            yych = this._charAt(cursor);
+            { gotoCase = 119; continue; };
+case 18:
+            {this.tokenType = "whitespace"; return cursor; }
+case 19:
+            ++cursor;
+            if ((yych = this._charAt(cursor)) == '=') { gotoCase = 117; continue; };
+case 20:
+            this.setLexCondition(this._lexConditions.NODIV);
+            {
+                    var token = this._line.charAt(cursorOnEnter);
+                    if (token === "{")
+                        this.tokenType = "block-start";
+                    else if (token === "}")
+                        this.tokenType = "block-end";
+                    else if (token === "(")
+                        this.tokenType = "brace-start";
+                    else this.tokenType = null;
+                    return cursor;
+                }
+case 21:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == '\n') { gotoCase = 16; continue; };
+            if (yych == '\r') { gotoCase = 16; continue; };
+            { gotoCase = 109; continue; };
+case 22:
+            yyaccept = 1;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 52; continue; };
+case 23:
+            {
+                    var token = this._line.substring(cursorOnEnter, cursor);
+                    if (WebInspector.SourceJavaScriptTokenizer.GlobalObjectValueProperties.hasOwnProperty(token))
+                        this.tokenType = WebInspector.SourceJavaScriptTokenizer.GlobalObjectValueProperties[token];
+                    else if (WebInspector.SourceJavaScriptTokenizer.Keywords[token] === true && token !== "__proto__")
+                        this.tokenType = "javascript-keyword";
+                    else
+                        this.tokenType = "javascript-ident";
+                    return cursor;
+                }
+case 24:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 25:
+            yych = this._charAt(++cursor);
+            if (yych == '&') { gotoCase = 45; continue; };
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 26:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == '\n') { gotoCase = 16; continue; };
+            if (yych == '\r') { gotoCase = 16; continue; };
+            { gotoCase = 98; continue; };
+case 27:
+            yych = this._charAt(++cursor);
+            { gotoCase = 20; continue; };
+case 28:
+            ++cursor;
+            { this.tokenType = "brace-end"; return cursor; }
+case 30:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 31:
+            yych = this._charAt(++cursor);
+            if (yych == '+') { gotoCase = 45; continue; };
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 32:
+            yych = this._charAt(++cursor);
+            if (yych == '-') { gotoCase = 45; continue; };
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 33:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 20; continue; };
+            if (yych <= '9') { gotoCase = 91; continue; };
+            { gotoCase = 20; continue; };
+case 34:
+            yyaccept = 2;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= '.') {
+                if (yych == '*') { gotoCase = 80; continue; };
+            } else {
+                if (yych <= '/') { gotoCase = 82; continue; };
+                if (yych == '=') { gotoCase = 79; continue; };
+            }
+case 35:
+            this.setLexCondition(this._lexConditions.NODIV);
+            { this.tokenType = null; return cursor; }
+case 36:
+            yyaccept = 3;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= 'E') {
+                if (yych <= '/') {
+                    if (yych == '.') { gotoCase = 65; continue; };
+                } else {
+                    if (yych <= '7') { gotoCase = 74; continue; };
+                    if (yych >= 'E') { gotoCase = 64; continue; };
+                }
+            } else {
+                if (yych <= 'd') {
+                    if (yych == 'X') { gotoCase = 76; continue; };
+                } else {
+                    if (yych <= 'e') { gotoCase = 64; continue; };
+                    if (yych == 'x') { gotoCase = 76; continue; };
+                }
+            }
+case 37:
+            { this.tokenType = "javascript-number"; return cursor; }
+case 38:
+            yyaccept = 3;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= '9') {
+                if (yych == '.') { gotoCase = 65; continue; };
+                if (yych <= '/') { gotoCase = 37; continue; };
+                { gotoCase = 62; continue; };
+            } else {
+                if (yych <= 'E') {
+                    if (yych <= 'D') { gotoCase = 37; continue; };
+                    { gotoCase = 64; continue; };
+                } else {
+                    if (yych == 'e') { gotoCase = 64; continue; };
+                    { gotoCase = 37; continue; };
+                }
+            }
+case 39:
+            yych = this._charAt(++cursor);
+            if (yych <= ';') { gotoCase = 20; continue; };
+            if (yych <= '<') { gotoCase = 61; continue; };
+            if (yych <= '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 40:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 60; continue; };
+            { gotoCase = 20; continue; };
+case 41:
+            yych = this._charAt(++cursor);
+            if (yych <= '<') { gotoCase = 20; continue; };
+            if (yych <= '=') { gotoCase = 45; continue; };
+            if (yych <= '>') { gotoCase = 58; continue; };
+            { gotoCase = 20; continue; };
+case 42:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == 'u') { gotoCase = 46; continue; };
+            { gotoCase = 16; continue; };
+case 43:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 44:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 45; continue; };
+            if (yych != '|') { gotoCase = 20; continue; };
+case 45:
+            yych = this._charAt(++cursor);
+            { gotoCase = 20; continue; };
+case 46:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych <= '9') { gotoCase = 48; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 48; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych <= 'f') { gotoCase = 48; continue; };
+            }
+case 47:
+            cursor = YYMARKER;
+            if (yyaccept <= 1) {
+                if (yyaccept <= 0) {
+                    { gotoCase = 16; continue; };
+                } else {
+                    { gotoCase = 23; continue; };
+                }
+            } else {
+                if (yyaccept <= 2) {
+                    { gotoCase = 35; continue; };
+                } else {
+                    { gotoCase = 37; continue; };
+                }
+            }
+case 48:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 49; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 49:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 50; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 50:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 51; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 51:
+            yyaccept = 1;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 52:
+            if (yych <= '[') {
+                if (yych <= '/') {
+                    if (yych == '$') { gotoCase = 51; continue; };
+                    { gotoCase = 23; continue; };
+                } else {
+                    if (yych <= '9') { gotoCase = 51; continue; };
+                    if (yych <= '@') { gotoCase = 23; continue; };
+                    if (yych <= 'Z') { gotoCase = 51; continue; };
+                    { gotoCase = 23; continue; };
+                }
+            } else {
+                if (yych <= '_') {
+                    if (yych <= '\\') { gotoCase = 53; continue; };
+                    if (yych <= '^') { gotoCase = 23; continue; };
+                    { gotoCase = 51; continue; };
+                } else {
+                    if (yych <= '`') { gotoCase = 23; continue; };
+                    if (yych <= 'z') { gotoCase = 51; continue; };
+                    if (yych <= String.fromCharCode(0x7F)) { gotoCase = 23; continue; };
+                    { gotoCase = 51; continue; };
+                }
+            }
+case 53:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych != 'u') { gotoCase = 47; continue; };
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 55; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 55:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 56; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 56:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 57; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 57:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych <= '9') { gotoCase = 51; continue; };
+                { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 51; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych <= 'f') { gotoCase = 51; continue; };
+                { gotoCase = 47; continue; };
+            }
+case 58:
+            yych = this._charAt(++cursor);
+            if (yych <= '<') { gotoCase = 20; continue; };
+            if (yych <= '=') { gotoCase = 45; continue; };
+            if (yych >= '?') { gotoCase = 20; continue; };
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 60:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 61:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 62:
+            yyaccept = 3;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '9') {
+                if (yych == '.') { gotoCase = 65; continue; };
+                if (yych <= '/') { gotoCase = 37; continue; };
+                { gotoCase = 62; continue; };
+            } else {
+                if (yych <= 'E') {
+                    if (yych <= 'D') { gotoCase = 37; continue; };
+                } else {
+                    if (yych != 'e') { gotoCase = 37; continue; };
+                }
+            }
+case 64:
+            yych = this._charAt(++cursor);
+            if (yych <= ',') {
+                if (yych == '+') { gotoCase = 71; continue; };
+                { gotoCase = 47; continue; };
+            } else {
+                if (yych <= '-') { gotoCase = 71; continue; };
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych <= '9') { gotoCase = 72; continue; };
+                { gotoCase = 47; continue; };
+            }
+case 65:
+            yyaccept = 3;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'D') {
+                if (yych <= '/') { gotoCase = 37; continue; };
+                if (yych <= '9') { gotoCase = 65; continue; };
+                { gotoCase = 37; continue; };
+            } else {
+                if (yych <= 'E') { gotoCase = 67; continue; };
+                if (yych != 'e') { gotoCase = 37; continue; };
+            }
+case 67:
+            yych = this._charAt(++cursor);
+            if (yych <= ',') {
+                if (yych != '+') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= '-') { gotoCase = 68; continue; };
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych <= '9') { gotoCase = 69; continue; };
+                { gotoCase = 47; continue; };
+            }
+case 68:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 47; continue; };
+            if (yych >= ':') { gotoCase = 47; continue; };
+case 69:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '/') { gotoCase = 37; continue; };
+            if (yych <= '9') { gotoCase = 69; continue; };
+            { gotoCase = 37; continue; };
+case 71:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 47; continue; };
+            if (yych >= ':') { gotoCase = 47; continue; };
+case 72:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '/') { gotoCase = 37; continue; };
+            if (yych <= '9') { gotoCase = 72; continue; };
+            { gotoCase = 37; continue; };
+case 74:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '/') { gotoCase = 37; continue; };
+            if (yych <= '7') { gotoCase = 74; continue; };
+            { gotoCase = 37; continue; };
+case 76:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 77; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 77:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 37; continue; };
+                if (yych <= '9') { gotoCase = 77; continue; };
+                { gotoCase = 37; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 77; continue; };
+                if (yych <= '`') { gotoCase = 37; continue; };
+                if (yych <= 'f') { gotoCase = 77; continue; };
+                { gotoCase = 37; continue; };
+            }
+case 79:
+            yych = this._charAt(++cursor);
+            { gotoCase = 35; continue; };
+case 80:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 87; continue; };
+                { gotoCase = 80; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 87; continue; };
+                if (yych == '*') { gotoCase = 85; continue; };
+                { gotoCase = 80; continue; };
+            }
+case 82:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 84; continue; };
+            if (yych != '\r') { gotoCase = 82; continue; };
+case 84:
+            { this.tokenType = "javascript-comment"; return cursor; }
+case 85:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '*') { gotoCase = 85; continue; };
+            if (yych == '/') { gotoCase = 89; continue; };
+            { gotoCase = 80; continue; };
+case 87:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.COMMENT);
+            { this.tokenType = "javascript-comment"; return cursor; }
+case 89:
+            ++cursor;
+            { this.tokenType = "javascript-comment"; return cursor; }
+case 91:
+            yyaccept = 3;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'D') {
+                if (yych <= '/') { gotoCase = 37; continue; };
+                if (yych <= '9') { gotoCase = 91; continue; };
+                { gotoCase = 37; continue; };
+            } else {
+                if (yych <= 'E') { gotoCase = 93; continue; };
+                if (yych != 'e') { gotoCase = 37; continue; };
+            }
+case 93:
+            yych = this._charAt(++cursor);
+            if (yych <= ',') {
+                if (yych != '+') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= '-') { gotoCase = 94; continue; };
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych <= '9') { gotoCase = 95; continue; };
+                { gotoCase = 47; continue; };
+            }
+case 94:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 47; continue; };
+            if (yych >= ':') { gotoCase = 47; continue; };
+case 95:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '/') { gotoCase = 37; continue; };
+            if (yych <= '9') { gotoCase = 95; continue; };
+            { gotoCase = 37; continue; };
+case 97:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 98:
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 47; continue; };
+                if (yych <= '\f') { gotoCase = 97; continue; };
+                { gotoCase = 47; continue; };
+            } else {
+                if (yych <= '\'') {
+                    if (yych <= '&') { gotoCase = 97; continue; };
+                    { gotoCase = 100; continue; };
+                } else {
+                    if (yych != '\\') { gotoCase = 97; continue; };
+                }
+            }
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'a') {
+                if (yych <= '!') {
+                    if (yych <= '\n') {
+                        if (yych <= '\t') { gotoCase = 47; continue; };
+                        { gotoCase = 103; continue; };
+                    } else {
+                        if (yych == '\r') { gotoCase = 103; continue; };
+                        { gotoCase = 47; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '"') { gotoCase = 97; continue; };
+                        if (yych <= '&') { gotoCase = 47; continue; };
+                        { gotoCase = 97; continue; };
+                    } else {
+                        if (yych == '\\') { gotoCase = 97; continue; };
+                        { gotoCase = 47; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'q') {
+                    if (yych <= 'f') {
+                        if (yych <= 'b') { gotoCase = 97; continue; };
+                        if (yych <= 'e') { gotoCase = 47; continue; };
+                        { gotoCase = 97; continue; };
+                    } else {
+                        if (yych == 'n') { gotoCase = 97; continue; };
+                        { gotoCase = 47; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych == 's') { gotoCase = 47; continue; };
+                        { gotoCase = 97; continue; };
+                    } else {
+                        if (yych <= 'u') { gotoCase = 102; continue; };
+                        if (yych <= 'v') { gotoCase = 97; continue; };
+                        { gotoCase = 47; continue; };
+                    }
+                }
+            }
+case 100:
+            ++cursor;
+            { this.tokenType = "javascript-string"; return cursor; }
+case 102:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych <= '9') { gotoCase = 105; continue; };
+                { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 105; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych <= 'f') { gotoCase = 105; continue; };
+                { gotoCase = 47; continue; };
+            }
+case 103:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.SSTRING);
+            { this.tokenType = "javascript-string"; return cursor; }
+case 105:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 106; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 106:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 107; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 107:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych <= '9') { gotoCase = 97; continue; };
+                { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 97; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych <= 'f') { gotoCase = 97; continue; };
+                { gotoCase = 47; continue; };
+            }
+case 108:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 109:
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 47; continue; };
+                if (yych <= '\f') { gotoCase = 108; continue; };
+                { gotoCase = 47; continue; };
+            } else {
+                if (yych <= '"') {
+                    if (yych <= '!') { gotoCase = 108; continue; };
+                    { gotoCase = 100; continue; };
+                } else {
+                    if (yych != '\\') { gotoCase = 108; continue; };
+                }
+            }
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'a') {
+                if (yych <= '!') {
+                    if (yych <= '\n') {
+                        if (yych <= '\t') { gotoCase = 47; continue; };
+                        { gotoCase = 112; continue; };
+                    } else {
+                        if (yych == '\r') { gotoCase = 112; continue; };
+                        { gotoCase = 47; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '"') { gotoCase = 108; continue; };
+                        if (yych <= '&') { gotoCase = 47; continue; };
+                        { gotoCase = 108; continue; };
+                    } else {
+                        if (yych == '\\') { gotoCase = 108; continue; };
+                        { gotoCase = 47; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'q') {
+                    if (yych <= 'f') {
+                        if (yych <= 'b') { gotoCase = 108; continue; };
+                        if (yych <= 'e') { gotoCase = 47; continue; };
+                        { gotoCase = 108; continue; };
+                    } else {
+                        if (yych == 'n') { gotoCase = 108; continue; };
+                        { gotoCase = 47; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych == 's') { gotoCase = 47; continue; };
+                        { gotoCase = 108; continue; };
+                    } else {
+                        if (yych <= 'u') { gotoCase = 111; continue; };
+                        if (yych <= 'v') { gotoCase = 108; continue; };
+                        { gotoCase = 47; continue; };
+                    }
+                }
+            }
+case 111:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych <= '9') { gotoCase = 114; continue; };
+                { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 114; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych <= 'f') { gotoCase = 114; continue; };
+                { gotoCase = 47; continue; };
+            }
+case 112:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.DSTRING);
+            { this.tokenType = "javascript-string"; return cursor; }
+case 114:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 115; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 115:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych >= ':') { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 116; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych >= 'g') { gotoCase = 47; continue; };
+            }
+case 116:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 47; continue; };
+                if (yych <= '9') { gotoCase = 108; continue; };
+                { gotoCase = 47; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 108; continue; };
+                if (yych <= '`') { gotoCase = 47; continue; };
+                if (yych <= 'f') { gotoCase = 108; continue; };
+                { gotoCase = 47; continue; };
+            }
+case 117:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 45; continue; };
+            { gotoCase = 20; continue; };
+case 118:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 119:
+            if (yych == ' ') { gotoCase = 118; continue; };
+            { gotoCase = 18; continue; };
+/* *********************************** */
+case this.case_DSTRING:
+            yych = this._charAt(cursor);
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 124; continue; };
+                if (yych <= '\f') { gotoCase = 123; continue; };
+                { gotoCase = 124; continue; };
+            } else {
+                if (yych <= '"') {
+                    if (yych <= '!') { gotoCase = 123; continue; };
+                    { gotoCase = 126; continue; };
+                } else {
+                    if (yych == '\\') { gotoCase = 128; continue; };
+                    { gotoCase = 123; continue; };
+                }
+            }
+case 122:
+            { this.tokenType = "javascript-string"; return cursor; }
+case 123:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 130; continue; };
+case 124:
+            ++cursor;
+case 125:
+            { this.tokenType = null; return cursor; }
+case 126:
+            ++cursor;
+case 127:
+            this.setLexCondition(this._lexConditions.NODIV);
+            { this.tokenType = "javascript-string"; return cursor; }
+case 128:
+            yyaccept = 1;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= 'e') {
+                if (yych <= '\'') {
+                    if (yych == '"') { gotoCase = 129; continue; };
+                    if (yych <= '&') { gotoCase = 125; continue; };
+                } else {
+                    if (yych <= '\\') {
+                        if (yych <= '[') { gotoCase = 125; continue; };
+                    } else {
+                        if (yych != 'b') { gotoCase = 125; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'r') {
+                    if (yych <= 'm') {
+                        if (yych >= 'g') { gotoCase = 125; continue; };
+                    } else {
+                        if (yych <= 'n') { gotoCase = 129; continue; };
+                        if (yych <= 'q') { gotoCase = 125; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych <= 's') { gotoCase = 125; continue; };
+                    } else {
+                        if (yych <= 'u') { gotoCase = 131; continue; };
+                        if (yych >= 'w') { gotoCase = 125; continue; };
+                    }
+                }
+            }
+case 129:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 130:
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 122; continue; };
+                if (yych <= '\f') { gotoCase = 129; continue; };
+                { gotoCase = 122; continue; };
+            } else {
+                if (yych <= '"') {
+                    if (yych <= '!') { gotoCase = 129; continue; };
+                    { gotoCase = 137; continue; };
+                } else {
+                    if (yych == '\\') { gotoCase = 136; continue; };
+                    { gotoCase = 129; continue; };
+                }
+            }
+case 131:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 132; continue; };
+                if (yych <= '9') { gotoCase = 133; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 133; continue; };
+                if (yych <= '`') { gotoCase = 132; continue; };
+                if (yych <= 'f') { gotoCase = 133; continue; };
+            }
+case 132:
+            cursor = YYMARKER;
+            if (yyaccept <= 0) {
+                { gotoCase = 122; continue; };
+            } else {
+                { gotoCase = 125; continue; };
+            }
+case 133:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 132; continue; };
+                if (yych >= ':') { gotoCase = 132; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 134; continue; };
+                if (yych <= '`') { gotoCase = 132; continue; };
+                if (yych >= 'g') { gotoCase = 132; continue; };
+            }
+case 134:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 132; continue; };
+                if (yych >= ':') { gotoCase = 132; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 135; continue; };
+                if (yych <= '`') { gotoCase = 132; continue; };
+                if (yych >= 'g') { gotoCase = 132; continue; };
+            }
+case 135:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 132; continue; };
+                if (yych <= '9') { gotoCase = 129; continue; };
+                { gotoCase = 132; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 129; continue; };
+                if (yych <= '`') { gotoCase = 132; continue; };
+                if (yych <= 'f') { gotoCase = 129; continue; };
+                { gotoCase = 132; continue; };
+            }
+case 136:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'e') {
+                if (yych <= '\'') {
+                    if (yych == '"') { gotoCase = 129; continue; };
+                    if (yych <= '&') { gotoCase = 132; continue; };
+                    { gotoCase = 129; continue; };
+                } else {
+                    if (yych <= '\\') {
+                        if (yych <= '[') { gotoCase = 132; continue; };
+                        { gotoCase = 129; continue; };
+                    } else {
+                        if (yych == 'b') { gotoCase = 129; continue; };
+                        { gotoCase = 132; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'r') {
+                    if (yych <= 'm') {
+                        if (yych <= 'f') { gotoCase = 129; continue; };
+                        { gotoCase = 132; continue; };
+                    } else {
+                        if (yych <= 'n') { gotoCase = 129; continue; };
+                        if (yych <= 'q') { gotoCase = 132; continue; };
+                        { gotoCase = 129; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych <= 's') { gotoCase = 132; continue; };
+                        { gotoCase = 129; continue; };
+                    } else {
+                        if (yych <= 'u') { gotoCase = 131; continue; };
+                        if (yych <= 'v') { gotoCase = 129; continue; };
+                        { gotoCase = 132; continue; };
+                    }
+                }
+            }
+case 137:
+            ++cursor;
+            yych = this._charAt(cursor);
+            { gotoCase = 127; continue; };
+/* *********************************** */
+case this.case_NODIV:
+            yych = this._charAt(cursor);
+            if (yych <= '9') {
+                if (yych <= '\'') {
+                    if (yych <= '"') {
+                        if (yych <= String.fromCharCode(0x1F)) { gotoCase = 140; continue; };
+                        if (yych <= ' ') { gotoCase = 142; continue; };
+                        if (yych <= '!') { gotoCase = 144; continue; };
+                        { gotoCase = 146; continue; };
+                    } else {
+                        if (yych <= '$') {
+                            if (yych >= '$') { gotoCase = 147; continue; };
+                        } else {
+                            if (yych <= '%') { gotoCase = 149; continue; };
+                            if (yych <= '&') { gotoCase = 150; continue; };
+                            { gotoCase = 151; continue; };
+                        }
+                    }
+                } else {
+                    if (yych <= ',') {
+                        if (yych <= ')') {
+                            if (yych <= '(') { gotoCase = 152; continue; };
+                            { gotoCase = 153; continue; };
+                        } else {
+                            if (yych <= '*') { gotoCase = 155; continue; };
+                            if (yych <= '+') { gotoCase = 156; continue; };
+                            { gotoCase = 152; continue; };
+                        }
+                    } else {
+                        if (yych <= '.') {
+                            if (yych <= '-') { gotoCase = 157; continue; };
+                            { gotoCase = 158; continue; };
+                        } else {
+                            if (yych <= '/') { gotoCase = 159; continue; };
+                            if (yych <= '0') { gotoCase = 160; continue; };
+                            { gotoCase = 162; continue; };
+                        }
+                    }
+                }
+            } else {
+                if (yych <= '\\') {
+                    if (yych <= '>') {
+                        if (yych <= ';') { gotoCase = 152; continue; };
+                        if (yych <= '<') { gotoCase = 163; continue; };
+                        if (yych <= '=') { gotoCase = 164; continue; };
+                        { gotoCase = 165; continue; };
+                    } else {
+                        if (yych <= '@') {
+                            if (yych <= '?') { gotoCase = 152; continue; };
+                        } else {
+                            if (yych <= 'Z') { gotoCase = 147; continue; };
+                            if (yych <= '[') { gotoCase = 152; continue; };
+                            { gotoCase = 166; continue; };
+                        }
+                    }
+                } else {
+                    if (yych <= 'z') {
+                        if (yych <= '^') {
+                            if (yych <= ']') { gotoCase = 152; continue; };
+                            { gotoCase = 167; continue; };
+                        } else {
+                            if (yych != '`') { gotoCase = 147; continue; };
+                        }
+                    } else {
+                        if (yych <= '|') {
+                            if (yych <= '{') { gotoCase = 152; continue; };
+                            { gotoCase = 168; continue; };
+                        } else {
+                            if (yych <= '~') { gotoCase = 152; continue; };
+                            if (yych >= 0x80) { gotoCase = 147; continue; };
+                        }
+                    }
+                }
+            }
+case 140:
+            ++cursor;
+case 141:
+            { this.tokenType = null; return cursor; }
+case 142:
+            ++cursor;
+            yych = this._charAt(cursor);
+            { gotoCase = 268; continue; };
+case 143:
+            {this.tokenType = "whitespace"; return cursor; }
+case 144:
+            ++cursor;
+            if ((yych = this._charAt(cursor)) == '=') { gotoCase = 266; continue; };
+case 145:
+            {
+                    var token = this._line.charAt(cursorOnEnter);
+                    if (token === "{")
+                        this.tokenType = "block-start";
+                    else if (token === "}")
+                        this.tokenType = "block-end";
+                    else if (token === "(")
+                        this.tokenType = "brace-start";
+                    else this.tokenType = null;
+                    return cursor;
+                }
+case 146:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == '\n') { gotoCase = 141; continue; };
+            if (yych == '\r') { gotoCase = 141; continue; };
+            { gotoCase = 258; continue; };
+case 147:
+            yyaccept = 1;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 176; continue; };
+case 148:
+            this.setLexCondition(this._lexConditions.DIV);
+            {
+                    var token = this._line.substring(cursorOnEnter, cursor);
+                    if (WebInspector.SourceJavaScriptTokenizer.GlobalObjectValueProperties.hasOwnProperty(token))
+                        this.tokenType = WebInspector.SourceJavaScriptTokenizer.GlobalObjectValueProperties[token];
+                    else if (WebInspector.SourceJavaScriptTokenizer.Keywords[token] === true && token !== "__proto__")
+                        this.tokenType = "javascript-keyword";
+                    else
+                        this.tokenType = "javascript-ident";
+                    return cursor;
+                }
+case 149:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 150:
+            yych = this._charAt(++cursor);
+            if (yych == '&') { gotoCase = 169; continue; };
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 151:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == '\n') { gotoCase = 141; continue; };
+            if (yych == '\r') { gotoCase = 141; continue; };
+            { gotoCase = 247; continue; };
+case 152:
+            yych = this._charAt(++cursor);
+            { gotoCase = 145; continue; };
+case 153:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.DIV);
+            { this.tokenType = "brace-end"; return cursor; }
+case 155:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 156:
+            yych = this._charAt(++cursor);
+            if (yych == '+') { gotoCase = 169; continue; };
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 157:
+            yych = this._charAt(++cursor);
+            if (yych == '-') { gotoCase = 169; continue; };
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 158:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 145; continue; };
+            if (yych <= '9') { gotoCase = 240; continue; };
+            { gotoCase = 145; continue; };
+case 159:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= '*') {
+                if (yych <= '\f') {
+                    if (yych == '\n') { gotoCase = 141; continue; };
+                    { gotoCase = 203; continue; };
+                } else {
+                    if (yych <= '\r') { gotoCase = 141; continue; };
+                    if (yych <= ')') { gotoCase = 203; continue; };
+                    { gotoCase = 208; continue; };
+                }
+            } else {
+                if (yych <= 'Z') {
+                    if (yych == '/') { gotoCase = 210; continue; };
+                    { gotoCase = 203; continue; };
+                } else {
+                    if (yych <= '[') { gotoCase = 206; continue; };
+                    if (yych <= '\\') { gotoCase = 205; continue; };
+                    if (yych <= ']') { gotoCase = 141; continue; };
+                    { gotoCase = 203; continue; };
+                }
+            }
+case 160:
+            yyaccept = 2;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= 'E') {
+                if (yych <= '/') {
+                    if (yych == '.') { gotoCase = 189; continue; };
+                } else {
+                    if (yych <= '7') { gotoCase = 198; continue; };
+                    if (yych >= 'E') { gotoCase = 188; continue; };
+                }
+            } else {
+                if (yych <= 'd') {
+                    if (yych == 'X') { gotoCase = 200; continue; };
+                } else {
+                    if (yych <= 'e') { gotoCase = 188; continue; };
+                    if (yych == 'x') { gotoCase = 200; continue; };
+                }
+            }
+case 161:
+            this.setLexCondition(this._lexConditions.DIV);
+            { this.tokenType = "javascript-number"; return cursor; }
+case 162:
+            yyaccept = 2;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= '9') {
+                if (yych == '.') { gotoCase = 189; continue; };
+                if (yych <= '/') { gotoCase = 161; continue; };
+                { gotoCase = 186; continue; };
+            } else {
+                if (yych <= 'E') {
+                    if (yych <= 'D') { gotoCase = 161; continue; };
+                    { gotoCase = 188; continue; };
+                } else {
+                    if (yych == 'e') { gotoCase = 188; continue; };
+                    { gotoCase = 161; continue; };
+                }
+            }
+case 163:
+            yych = this._charAt(++cursor);
+            if (yych <= ';') { gotoCase = 145; continue; };
+            if (yych <= '<') { gotoCase = 185; continue; };
+            if (yych <= '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 164:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 184; continue; };
+            { gotoCase = 145; continue; };
+case 165:
+            yych = this._charAt(++cursor);
+            if (yych <= '<') { gotoCase = 145; continue; };
+            if (yych <= '=') { gotoCase = 169; continue; };
+            if (yych <= '>') { gotoCase = 182; continue; };
+            { gotoCase = 145; continue; };
+case 166:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych == 'u') { gotoCase = 170; continue; };
+            { gotoCase = 141; continue; };
+case 167:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 168:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 169; continue; };
+            if (yych != '|') { gotoCase = 145; continue; };
+case 169:
+            yych = this._charAt(++cursor);
+            { gotoCase = 145; continue; };
+case 170:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych <= '9') { gotoCase = 172; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 172; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych <= 'f') { gotoCase = 172; continue; };
+            }
+case 171:
+            cursor = YYMARKER;
+            if (yyaccept <= 1) {
+                if (yyaccept <= 0) {
+                    { gotoCase = 141; continue; };
+                } else {
+                    { gotoCase = 148; continue; };
+                }
+            } else {
+                if (yyaccept <= 2) {
+                    { gotoCase = 161; continue; };
+                } else {
+                    { gotoCase = 223; continue; };
+                }
+            }
+case 172:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 173; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 173:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 174; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 174:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 175; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 175:
+            yyaccept = 1;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 176:
+            if (yych <= '[') {
+                if (yych <= '/') {
+                    if (yych == '$') { gotoCase = 175; continue; };
+                    { gotoCase = 148; continue; };
+                } else {
+                    if (yych <= '9') { gotoCase = 175; continue; };
+                    if (yych <= '@') { gotoCase = 148; continue; };
+                    if (yych <= 'Z') { gotoCase = 175; continue; };
+                    { gotoCase = 148; continue; };
+                }
+            } else {
+                if (yych <= '_') {
+                    if (yych <= '\\') { gotoCase = 177; continue; };
+                    if (yych <= '^') { gotoCase = 148; continue; };
+                    { gotoCase = 175; continue; };
+                } else {
+                    if (yych <= '`') { gotoCase = 148; continue; };
+                    if (yych <= 'z') { gotoCase = 175; continue; };
+                    if (yych <= String.fromCharCode(0x7F)) { gotoCase = 148; continue; };
+                    { gotoCase = 175; continue; };
+                }
+            }
+case 177:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych != 'u') { gotoCase = 171; continue; };
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 179; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 179:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 180; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 180:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 181; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 181:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych <= '9') { gotoCase = 175; continue; };
+                { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 175; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych <= 'f') { gotoCase = 175; continue; };
+                { gotoCase = 171; continue; };
+            }
+case 182:
+            yych = this._charAt(++cursor);
+            if (yych <= '<') { gotoCase = 145; continue; };
+            if (yych <= '=') { gotoCase = 169; continue; };
+            if (yych >= '?') { gotoCase = 145; continue; };
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 184:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 185:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 186:
+            yyaccept = 2;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '9') {
+                if (yych == '.') { gotoCase = 189; continue; };
+                if (yych <= '/') { gotoCase = 161; continue; };
+                { gotoCase = 186; continue; };
+            } else {
+                if (yych <= 'E') {
+                    if (yych <= 'D') { gotoCase = 161; continue; };
+                } else {
+                    if (yych != 'e') { gotoCase = 161; continue; };
+                }
+            }
+case 188:
+            yych = this._charAt(++cursor);
+            if (yych <= ',') {
+                if (yych == '+') { gotoCase = 195; continue; };
+                { gotoCase = 171; continue; };
+            } else {
+                if (yych <= '-') { gotoCase = 195; continue; };
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych <= '9') { gotoCase = 196; continue; };
+                { gotoCase = 171; continue; };
+            }
+case 189:
+            yyaccept = 2;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'D') {
+                if (yych <= '/') { gotoCase = 161; continue; };
+                if (yych <= '9') { gotoCase = 189; continue; };
+                { gotoCase = 161; continue; };
+            } else {
+                if (yych <= 'E') { gotoCase = 191; continue; };
+                if (yych != 'e') { gotoCase = 161; continue; };
+            }
+case 191:
+            yych = this._charAt(++cursor);
+            if (yych <= ',') {
+                if (yych != '+') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= '-') { gotoCase = 192; continue; };
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych <= '9') { gotoCase = 193; continue; };
+                { gotoCase = 171; continue; };
+            }
+case 192:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 171; continue; };
+            if (yych >= ':') { gotoCase = 171; continue; };
+case 193:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '/') { gotoCase = 161; continue; };
+            if (yych <= '9') { gotoCase = 193; continue; };
+            { gotoCase = 161; continue; };
+case 195:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 171; continue; };
+            if (yych >= ':') { gotoCase = 171; continue; };
+case 196:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '/') { gotoCase = 161; continue; };
+            if (yych <= '9') { gotoCase = 196; continue; };
+            { gotoCase = 161; continue; };
+case 198:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '/') { gotoCase = 161; continue; };
+            if (yych <= '7') { gotoCase = 198; continue; };
+            { gotoCase = 161; continue; };
+case 200:
+            yych = this._charAt(++cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 201; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 201:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 161; continue; };
+                if (yych <= '9') { gotoCase = 201; continue; };
+                { gotoCase = 161; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 201; continue; };
+                if (yych <= '`') { gotoCase = 161; continue; };
+                if (yych <= 'f') { gotoCase = 201; continue; };
+                { gotoCase = 161; continue; };
+            }
+case 203:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '.') {
+                if (yych <= '\n') {
+                    if (yych <= '\t') { gotoCase = 203; continue; };
+                    { gotoCase = 171; continue; };
+                } else {
+                    if (yych == '\r') { gotoCase = 171; continue; };
+                    { gotoCase = 203; continue; };
+                }
+            } else {
+                if (yych <= '[') {
+                    if (yych <= '/') { gotoCase = 226; continue; };
+                    if (yych <= 'Z') { gotoCase = 203; continue; };
+                    { gotoCase = 234; continue; };
+                } else {
+                    if (yych <= '\\') { gotoCase = 233; continue; };
+                    if (yych <= ']') { gotoCase = 171; continue; };
+                    { gotoCase = 203; continue; };
+                }
+            }
+case 205:
+            yych = this._charAt(++cursor);
+            if (yych == '\n') { gotoCase = 171; continue; };
+            if (yych == '\r') { gotoCase = 171; continue; };
+            { gotoCase = 203; continue; };
+case 206:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '*') {
+                if (yych <= '\f') {
+                    if (yych == '\n') { gotoCase = 171; continue; };
+                    { gotoCase = 206; continue; };
+                } else {
+                    if (yych <= '\r') { gotoCase = 171; continue; };
+                    if (yych <= ')') { gotoCase = 206; continue; };
+                    { gotoCase = 171; continue; };
+                }
+            } else {
+                if (yych <= '[') {
+                    if (yych == '/') { gotoCase = 171; continue; };
+                    { gotoCase = 206; continue; };
+                } else {
+                    if (yych <= '\\') { gotoCase = 221; continue; };
+                    if (yych <= ']') { gotoCase = 219; continue; };
+                    { gotoCase = 206; continue; };
+                }
+            }
+case 208:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '\f') {
+                if (yych == '\n') { gotoCase = 215; continue; };
+                { gotoCase = 208; continue; };
+            } else {
+                if (yych <= '\r') { gotoCase = 215; continue; };
+                if (yych == '*') { gotoCase = 213; continue; };
+                { gotoCase = 208; continue; };
+            }
+case 210:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 212; continue; };
+            if (yych != '\r') { gotoCase = 210; continue; };
+case 212:
+            { this.tokenType = "javascript-comment"; return cursor; }
+case 213:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '*') { gotoCase = 213; continue; };
+            if (yych == '/') { gotoCase = 217; continue; };
+            { gotoCase = 208; continue; };
+case 215:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.COMMENT);
+            { this.tokenType = "javascript-comment"; return cursor; }
+case 217:
+            ++cursor;
+            { this.tokenType = "javascript-comment"; return cursor; }
+case 219:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '*') {
+                if (yych <= '\f') {
+                    if (yych == '\n') { gotoCase = 171; continue; };
+                    { gotoCase = 219; continue; };
+                } else {
+                    if (yych <= '\r') { gotoCase = 171; continue; };
+                    if (yych <= ')') { gotoCase = 219; continue; };
+                    { gotoCase = 203; continue; };
+                }
+            } else {
+                if (yych <= 'Z') {
+                    if (yych == '/') { gotoCase = 226; continue; };
+                    { gotoCase = 219; continue; };
+                } else {
+                    if (yych <= '[') { gotoCase = 224; continue; };
+                    if (yych <= '\\') { gotoCase = 222; continue; };
+                    { gotoCase = 219; continue; };
+                }
+            }
+case 221:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 171; continue; };
+            if (yych == '\r') { gotoCase = 171; continue; };
+            { gotoCase = 206; continue; };
+case 222:
+            yyaccept = 3;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 223; continue; };
+            if (yych != '\r') { gotoCase = 219; continue; };
+case 223:
+            this.setLexCondition(this._lexConditions.REGEX);
+            { this.tokenType = "javascript-regexp"; return cursor; }
+case 224:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '*') {
+                if (yych <= '\f') {
+                    if (yych == '\n') { gotoCase = 171; continue; };
+                    { gotoCase = 224; continue; };
+                } else {
+                    if (yych <= '\r') { gotoCase = 171; continue; };
+                    if (yych <= ')') { gotoCase = 224; continue; };
+                    { gotoCase = 171; continue; };
+                }
+            } else {
+                if (yych <= '[') {
+                    if (yych == '/') { gotoCase = 171; continue; };
+                    { gotoCase = 224; continue; };
+                } else {
+                    if (yych <= '\\') { gotoCase = 231; continue; };
+                    if (yych <= ']') { gotoCase = 229; continue; };
+                    { gotoCase = 224; continue; };
+                }
+            }
+case 226:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'h') {
+                if (yych == 'g') { gotoCase = 226; continue; };
+            } else {
+                if (yych <= 'i') { gotoCase = 226; continue; };
+                if (yych == 'm') { gotoCase = 226; continue; };
+            }
+            { this.tokenType = "javascript-regexp"; return cursor; }
+case 229:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '*') {
+                if (yych <= '\f') {
+                    if (yych == '\n') { gotoCase = 171; continue; };
+                    { gotoCase = 229; continue; };
+                } else {
+                    if (yych <= '\r') { gotoCase = 171; continue; };
+                    if (yych <= ')') { gotoCase = 229; continue; };
+                    { gotoCase = 203; continue; };
+                }
+            } else {
+                if (yych <= 'Z') {
+                    if (yych == '/') { gotoCase = 226; continue; };
+                    { gotoCase = 229; continue; };
+                } else {
+                    if (yych <= '[') { gotoCase = 224; continue; };
+                    if (yych <= '\\') { gotoCase = 232; continue; };
+                    { gotoCase = 229; continue; };
+                }
+            }
+case 231:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 171; continue; };
+            if (yych == '\r') { gotoCase = 171; continue; };
+            { gotoCase = 224; continue; };
+case 232:
+            yyaccept = 3;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 223; continue; };
+            if (yych == '\r') { gotoCase = 223; continue; };
+            { gotoCase = 229; continue; };
+case 233:
+            yyaccept = 3;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 223; continue; };
+            if (yych == '\r') { gotoCase = 223; continue; };
+            { gotoCase = 203; continue; };
+case 234:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '*') {
+                if (yych <= '\f') {
+                    if (yych == '\n') { gotoCase = 171; continue; };
+                    { gotoCase = 234; continue; };
+                } else {
+                    if (yych <= '\r') { gotoCase = 171; continue; };
+                    if (yych <= ')') { gotoCase = 234; continue; };
+                    { gotoCase = 171; continue; };
+                }
+            } else {
+                if (yych <= '[') {
+                    if (yych == '/') { gotoCase = 171; continue; };
+                    { gotoCase = 234; continue; };
+                } else {
+                    if (yych <= '\\') { gotoCase = 238; continue; };
+                    if (yych >= '^') { gotoCase = 234; continue; };
+                }
+            }
+case 236:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '*') {
+                if (yych <= '\f') {
+                    if (yych == '\n') { gotoCase = 171; continue; };
+                    { gotoCase = 236; continue; };
+                } else {
+                    if (yych <= '\r') { gotoCase = 171; continue; };
+                    if (yych <= ')') { gotoCase = 236; continue; };
+                    { gotoCase = 203; continue; };
+                }
+            } else {
+                if (yych <= 'Z') {
+                    if (yych == '/') { gotoCase = 226; continue; };
+                    { gotoCase = 236; continue; };
+                } else {
+                    if (yych <= '[') { gotoCase = 234; continue; };
+                    if (yych <= '\\') { gotoCase = 239; continue; };
+                    { gotoCase = 236; continue; };
+                }
+            }
+case 238:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 171; continue; };
+            if (yych == '\r') { gotoCase = 171; continue; };
+            { gotoCase = 234; continue; };
+case 239:
+            yyaccept = 3;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 223; continue; };
+            if (yych == '\r') { gotoCase = 223; continue; };
+            { gotoCase = 236; continue; };
+case 240:
+            yyaccept = 2;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'D') {
+                if (yych <= '/') { gotoCase = 161; continue; };
+                if (yych <= '9') { gotoCase = 240; continue; };
+                { gotoCase = 161; continue; };
+            } else {
+                if (yych <= 'E') { gotoCase = 242; continue; };
+                if (yych != 'e') { gotoCase = 161; continue; };
+            }
+case 242:
+            yych = this._charAt(++cursor);
+            if (yych <= ',') {
+                if (yych != '+') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= '-') { gotoCase = 243; continue; };
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych <= '9') { gotoCase = 244; continue; };
+                { gotoCase = 171; continue; };
+            }
+case 243:
+            yych = this._charAt(++cursor);
+            if (yych <= '/') { gotoCase = 171; continue; };
+            if (yych >= ':') { gotoCase = 171; continue; };
+case 244:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '/') { gotoCase = 161; continue; };
+            if (yych <= '9') { gotoCase = 244; continue; };
+            { gotoCase = 161; continue; };
+case 246:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 247:
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 171; continue; };
+                if (yych <= '\f') { gotoCase = 246; continue; };
+                { gotoCase = 171; continue; };
+            } else {
+                if (yych <= '\'') {
+                    if (yych <= '&') { gotoCase = 246; continue; };
+                    { gotoCase = 249; continue; };
+                } else {
+                    if (yych != '\\') { gotoCase = 246; continue; };
+                }
+            }
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'a') {
+                if (yych <= '!') {
+                    if (yych <= '\n') {
+                        if (yych <= '\t') { gotoCase = 171; continue; };
+                        { gotoCase = 252; continue; };
+                    } else {
+                        if (yych == '\r') { gotoCase = 252; continue; };
+                        { gotoCase = 171; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '"') { gotoCase = 246; continue; };
+                        if (yych <= '&') { gotoCase = 171; continue; };
+                        { gotoCase = 246; continue; };
+                    } else {
+                        if (yych == '\\') { gotoCase = 246; continue; };
+                        { gotoCase = 171; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'q') {
+                    if (yych <= 'f') {
+                        if (yych <= 'b') { gotoCase = 246; continue; };
+                        if (yych <= 'e') { gotoCase = 171; continue; };
+                        { gotoCase = 246; continue; };
+                    } else {
+                        if (yych == 'n') { gotoCase = 246; continue; };
+                        { gotoCase = 171; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych == 's') { gotoCase = 171; continue; };
+                        { gotoCase = 246; continue; };
+                    } else {
+                        if (yych <= 'u') { gotoCase = 251; continue; };
+                        if (yych <= 'v') { gotoCase = 246; continue; };
+                        { gotoCase = 171; continue; };
+                    }
+                }
+            }
+case 249:
+            ++cursor;
+            { this.tokenType = "javascript-string"; return cursor; }
+case 251:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych <= '9') { gotoCase = 254; continue; };
+                { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 254; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych <= 'f') { gotoCase = 254; continue; };
+                { gotoCase = 171; continue; };
+            }
+case 252:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.SSTRING);
+            { this.tokenType = "javascript-string"; return cursor; }
+case 254:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 255; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 255:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 256; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 256:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych <= '9') { gotoCase = 246; continue; };
+                { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 246; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych <= 'f') { gotoCase = 246; continue; };
+                { gotoCase = 171; continue; };
+            }
+case 257:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 258:
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 171; continue; };
+                if (yych <= '\f') { gotoCase = 257; continue; };
+                { gotoCase = 171; continue; };
+            } else {
+                if (yych <= '"') {
+                    if (yych <= '!') { gotoCase = 257; continue; };
+                    { gotoCase = 249; continue; };
+                } else {
+                    if (yych != '\\') { gotoCase = 257; continue; };
+                }
+            }
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'a') {
+                if (yych <= '!') {
+                    if (yych <= '\n') {
+                        if (yych <= '\t') { gotoCase = 171; continue; };
+                        { gotoCase = 261; continue; };
+                    } else {
+                        if (yych == '\r') { gotoCase = 261; continue; };
+                        { gotoCase = 171; continue; };
+                    }
+                } else {
+                    if (yych <= '\'') {
+                        if (yych <= '"') { gotoCase = 257; continue; };
+                        if (yych <= '&') { gotoCase = 171; continue; };
+                        { gotoCase = 257; continue; };
+                    } else {
+                        if (yych == '\\') { gotoCase = 257; continue; };
+                        { gotoCase = 171; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'q') {
+                    if (yych <= 'f') {
+                        if (yych <= 'b') { gotoCase = 257; continue; };
+                        if (yych <= 'e') { gotoCase = 171; continue; };
+                        { gotoCase = 257; continue; };
+                    } else {
+                        if (yych == 'n') { gotoCase = 257; continue; };
+                        { gotoCase = 171; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych == 's') { gotoCase = 171; continue; };
+                        { gotoCase = 257; continue; };
+                    } else {
+                        if (yych <= 'u') { gotoCase = 260; continue; };
+                        if (yych <= 'v') { gotoCase = 257; continue; };
+                        { gotoCase = 171; continue; };
+                    }
+                }
+            }
+case 260:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych <= '9') { gotoCase = 263; continue; };
+                { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 263; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych <= 'f') { gotoCase = 263; continue; };
+                { gotoCase = 171; continue; };
+            }
+case 261:
+            ++cursor;
+            this.setLexCondition(this._lexConditions.DSTRING);
+            { this.tokenType = "javascript-string"; return cursor; }
+case 263:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 264; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 264:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych >= ':') { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 265; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych >= 'g') { gotoCase = 171; continue; };
+            }
+case 265:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 171; continue; };
+                if (yych <= '9') { gotoCase = 257; continue; };
+                { gotoCase = 171; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 257; continue; };
+                if (yych <= '`') { gotoCase = 171; continue; };
+                if (yych <= 'f') { gotoCase = 257; continue; };
+                { gotoCase = 171; continue; };
+            }
+case 266:
+            yych = this._charAt(++cursor);
+            if (yych == '=') { gotoCase = 169; continue; };
+            { gotoCase = 145; continue; };
+case 267:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 268:
+            if (yych == ' ') { gotoCase = 267; continue; };
+            { gotoCase = 143; continue; };
+/* *********************************** */
+case this.case_REGEX:
+            yych = this._charAt(cursor);
+            if (yych <= '.') {
+                if (yych <= '\n') {
+                    if (yych <= '\t') { gotoCase = 272; continue; };
+                    { gotoCase = 273; continue; };
+                } else {
+                    if (yych == '\r') { gotoCase = 273; continue; };
+                    { gotoCase = 272; continue; };
+                }
+            } else {
+                if (yych <= '[') {
+                    if (yych <= '/') { gotoCase = 275; continue; };
+                    if (yych <= 'Z') { gotoCase = 272; continue; };
+                    { gotoCase = 277; continue; };
+                } else {
+                    if (yych <= '\\') { gotoCase = 278; continue; };
+                    if (yych <= ']') { gotoCase = 273; continue; };
+                    { gotoCase = 272; continue; };
+                }
+            }
+case 271:
+            { this.tokenType = "javascript-regexp"; return cursor; }
+case 272:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 280; continue; };
+case 273:
+            ++cursor;
+case 274:
+            { this.tokenType = null; return cursor; }
+case 275:
+            ++cursor;
+            yych = this._charAt(cursor);
+            { gotoCase = 286; continue; };
+case 276:
+            this.setLexCondition(this._lexConditions.NODIV);
+            { this.tokenType = "javascript-regexp"; return cursor; }
+case 277:
+            yyaccept = 1;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 274; continue; };
+                if (yych <= '\f') { gotoCase = 284; continue; };
+                { gotoCase = 274; continue; };
+            } else {
+                if (yych <= '*') {
+                    if (yych <= ')') { gotoCase = 284; continue; };
+                    { gotoCase = 274; continue; };
+                } else {
+                    if (yych == '/') { gotoCase = 274; continue; };
+                    { gotoCase = 284; continue; };
+                }
+            }
+case 278:
+            yych = this._charAt(++cursor);
+            if (yych == '\n') { gotoCase = 274; continue; };
+            if (yych == '\r') { gotoCase = 274; continue; };
+case 279:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 280:
+            if (yych <= '.') {
+                if (yych <= '\n') {
+                    if (yych <= '\t') { gotoCase = 279; continue; };
+                    { gotoCase = 271; continue; };
+                } else {
+                    if (yych == '\r') { gotoCase = 271; continue; };
+                    { gotoCase = 279; continue; };
+                }
+            } else {
+                if (yych <= '[') {
+                    if (yych <= '/') { gotoCase = 285; continue; };
+                    if (yych <= 'Z') { gotoCase = 279; continue; };
+                    { gotoCase = 283; continue; };
+                } else {
+                    if (yych <= '\\') { gotoCase = 281; continue; };
+                    if (yych <= ']') { gotoCase = 271; continue; };
+                    { gotoCase = 279; continue; };
+                }
+            }
+case 281:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 282; continue; };
+            if (yych != '\r') { gotoCase = 279; continue; };
+case 282:
+            cursor = YYMARKER;
+            if (yyaccept <= 0) {
+                { gotoCase = 271; continue; };
+            } else {
+                { gotoCase = 274; continue; };
+            }
+case 283:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 284:
+            if (yych <= '*') {
+                if (yych <= '\f') {
+                    if (yych == '\n') { gotoCase = 282; continue; };
+                    { gotoCase = 283; continue; };
+                } else {
+                    if (yych <= '\r') { gotoCase = 282; continue; };
+                    if (yych <= ')') { gotoCase = 283; continue; };
+                    { gotoCase = 282; continue; };
+                }
+            } else {
+                if (yych <= '[') {
+                    if (yych == '/') { gotoCase = 282; continue; };
+                    { gotoCase = 283; continue; };
+                } else {
+                    if (yych <= '\\') { gotoCase = 289; continue; };
+                    if (yych <= ']') { gotoCase = 287; continue; };
+                    { gotoCase = 283; continue; };
+                }
+            }
+case 285:
+            ++cursor;
+            yych = this._charAt(cursor);
+case 286:
+            if (yych <= 'h') {
+                if (yych == 'g') { gotoCase = 285; continue; };
+                { gotoCase = 276; continue; };
+            } else {
+                if (yych <= 'i') { gotoCase = 285; continue; };
+                if (yych == 'm') { gotoCase = 285; continue; };
+                { gotoCase = 276; continue; };
+            }
+case 287:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '*') {
+                if (yych <= '\f') {
+                    if (yych == '\n') { gotoCase = 271; continue; };
+                    { gotoCase = 287; continue; };
+                } else {
+                    if (yych <= '\r') { gotoCase = 271; continue; };
+                    if (yych <= ')') { gotoCase = 287; continue; };
+                    { gotoCase = 279; continue; };
+                }
+            } else {
+                if (yych <= 'Z') {
+                    if (yych == '/') { gotoCase = 285; continue; };
+                    { gotoCase = 287; continue; };
+                } else {
+                    if (yych <= '[') { gotoCase = 283; continue; };
+                    if (yych <= '\\') { gotoCase = 290; continue; };
+                    { gotoCase = 287; continue; };
+                }
+            }
+case 289:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 282; continue; };
+            if (yych == '\r') { gotoCase = 282; continue; };
+            { gotoCase = 283; continue; };
+case 290:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych == '\n') { gotoCase = 282; continue; };
+            if (yych == '\r') { gotoCase = 282; continue; };
+            { gotoCase = 287; continue; };
+/* *********************************** */
+case this.case_SSTRING:
+            yych = this._charAt(cursor);
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 295; continue; };
+                if (yych <= '\f') { gotoCase = 294; continue; };
+                { gotoCase = 295; continue; };
+            } else {
+                if (yych <= '\'') {
+                    if (yych <= '&') { gotoCase = 294; continue; };
+                    { gotoCase = 297; continue; };
+                } else {
+                    if (yych == '\\') { gotoCase = 299; continue; };
+                    { gotoCase = 294; continue; };
+                }
+            }
+case 293:
+            { this.tokenType = "javascript-string"; return cursor; }
+case 294:
+            yyaccept = 0;
+            yych = this._charAt(YYMARKER = ++cursor);
+            { gotoCase = 301; continue; };
+case 295:
+            ++cursor;
+case 296:
+            { this.tokenType = null; return cursor; }
+case 297:
+            ++cursor;
+case 298:
+            this.setLexCondition(this._lexConditions.NODIV);
+            { this.tokenType = "javascript-string"; return cursor; }
+case 299:
+            yyaccept = 1;
+            yych = this._charAt(YYMARKER = ++cursor);
+            if (yych <= 'e') {
+                if (yych <= '\'') {
+                    if (yych == '"') { gotoCase = 300; continue; };
+                    if (yych <= '&') { gotoCase = 296; continue; };
+                } else {
+                    if (yych <= '\\') {
+                        if (yych <= '[') { gotoCase = 296; continue; };
+                    } else {
+                        if (yych != 'b') { gotoCase = 296; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'r') {
+                    if (yych <= 'm') {
+                        if (yych >= 'g') { gotoCase = 296; continue; };
+                    } else {
+                        if (yych <= 'n') { gotoCase = 300; continue; };
+                        if (yych <= 'q') { gotoCase = 296; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych <= 's') { gotoCase = 296; continue; };
+                    } else {
+                        if (yych <= 'u') { gotoCase = 302; continue; };
+                        if (yych >= 'w') { gotoCase = 296; continue; };
+                    }
+                }
+            }
+case 300:
+            yyaccept = 0;
+            YYMARKER = ++cursor;
+            yych = this._charAt(cursor);
+case 301:
+            if (yych <= '\r') {
+                if (yych == '\n') { gotoCase = 293; continue; };
+                if (yych <= '\f') { gotoCase = 300; continue; };
+                { gotoCase = 293; continue; };
+            } else {
+                if (yych <= '\'') {
+                    if (yych <= '&') { gotoCase = 300; continue; };
+                    { gotoCase = 308; continue; };
+                } else {
+                    if (yych == '\\') { gotoCase = 307; continue; };
+                    { gotoCase = 300; continue; };
+                }
+            }
+case 302:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 303; continue; };
+                if (yych <= '9') { gotoCase = 304; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 304; continue; };
+                if (yych <= '`') { gotoCase = 303; continue; };
+                if (yych <= 'f') { gotoCase = 304; continue; };
+            }
+case 303:
+            cursor = YYMARKER;
+            if (yyaccept <= 0) {
+                { gotoCase = 293; continue; };
+            } else {
+                { gotoCase = 296; continue; };
+            }
+case 304:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 303; continue; };
+                if (yych >= ':') { gotoCase = 303; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 305; continue; };
+                if (yych <= '`') { gotoCase = 303; continue; };
+                if (yych >= 'g') { gotoCase = 303; continue; };
+            }
+case 305:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 303; continue; };
+                if (yych >= ':') { gotoCase = 303; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 306; continue; };
+                if (yych <= '`') { gotoCase = 303; continue; };
+                if (yych >= 'g') { gotoCase = 303; continue; };
+            }
+case 306:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= '@') {
+                if (yych <= '/') { gotoCase = 303; continue; };
+                if (yych <= '9') { gotoCase = 300; continue; };
+                { gotoCase = 303; continue; };
+            } else {
+                if (yych <= 'F') { gotoCase = 300; continue; };
+                if (yych <= '`') { gotoCase = 303; continue; };
+                if (yych <= 'f') { gotoCase = 300; continue; };
+                { gotoCase = 303; continue; };
+            }
+case 307:
+            ++cursor;
+            yych = this._charAt(cursor);
+            if (yych <= 'e') {
+                if (yych <= '\'') {
+                    if (yych == '"') { gotoCase = 300; continue; };
+                    if (yych <= '&') { gotoCase = 303; continue; };
+                    { gotoCase = 300; continue; };
+                } else {
+                    if (yych <= '\\') {
+                        if (yych <= '[') { gotoCase = 303; continue; };
+                        { gotoCase = 300; continue; };
+                    } else {
+                        if (yych == 'b') { gotoCase = 300; continue; };
+                        { gotoCase = 303; continue; };
+                    }
+                }
+            } else {
+                if (yych <= 'r') {
+                    if (yych <= 'm') {
+                        if (yych <= 'f') { gotoCase = 300; continue; };
+                        { gotoCase = 303; continue; };
+                    } else {
+                        if (yych <= 'n') { gotoCase = 300; continue; };
+                        if (yych <= 'q') { gotoCase = 303; continue; };
+                        { gotoCase = 300; continue; };
+                    }
+                } else {
+                    if (yych <= 't') {
+                        if (yych <= 's') { gotoCase = 303; continue; };
+                        { gotoCase = 300; continue; };
+                    } else {
+                        if (yych <= 'u') { gotoCase = 302; continue; };
+                        if (yych <= 'v') { gotoCase = 300; continue; };
+                        { gotoCase = 303; continue; };
+                    }
+                }
+            }
+case 308:
+            ++cursor;
+            yych = this._charAt(cursor);
+            { gotoCase = 298; continue; };
+        }
+
+        }
+    },
+
+    __proto__: WebInspector.SourceTokenizer.prototype
+}
diff --git a/Source/devtools/front_end/SourceJavaScriptTokenizer.re2js b/Source/devtools/front_end/SourceJavaScriptTokenizer.re2js
new file mode 100644
index 0000000..6b48c05
--- /dev/null
+++ b/Source/devtools/front_end/SourceJavaScriptTokenizer.re2js
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+/*
+ * Generate js file as follows:
+re2c -isc devtools/front_end/SourceJavaScriptTokenizer.re2js \
+  | sed 's|^yy\([^:]*\)*\:|case \1:|' \
+  | sed 's|[*]cursor[+][+]|this._charAt(cursor++)|' \
+  | sed 's|[[*][+][+]cursor|this._charAt(++cursor)|' \
+  | sed 's|[*]cursor|this._charAt(cursor)|' \
+  | sed 's|yych = \*\([^;]*\)|yych = this._charAt\1|' \
+  | sed 's|goto case \([^;]*\)|{ gotoCase = \1; continue; }|' \
+  | sed 's|yych <= \(0x[0-9a-fA-F]*\)|yych \<\= String.fromCharCode(\1)|' \
+  | sed 's|unsigned\ int|var|' \
+  | sed 's|var\ yych|case 1: var yych|' > devtools/front_end/SourceJavaScriptTokenizer.js
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SourceTokenizer}
+ */
+WebInspector.SourceJavaScriptTokenizer = function()
+{
+    WebInspector.SourceTokenizer.call(this);
+
+    this._lexConditions = {
+        DIV: 0,
+        NODIV: 1,
+        COMMENT: 2,
+        DSTRING: 3,
+        SSTRING: 4,
+        REGEX: 5
+    };
+
+    this.case_DIV = 1000;
+    this.case_NODIV = 1001;
+    this.case_COMMENT = 1002;
+    this.case_DSTRING = 1003;
+    this.case_SSTRING = 1004;
+    this.case_REGEX = 1005;
+
+    this.condition = this.createInitialCondition();
+}
+
+WebInspector.SourceJavaScriptTokenizer.Keywords = [
+        "null", "true", "false", "break", "case", "catch", "const", "default", "finally", "for",
+        "instanceof", "new", "var", "continue", "function", "return", "void", "delete", "if",
+        "this", "do", "while", "else", "in", "switch", "throw", "try", "typeof", "debugger",
+        "class", "enum", "export", "extends", "import", "super", "get", "set", "with"
+    ].keySet();
+
+WebInspector.SourceJavaScriptTokenizer.GlobalObjectValueProperties = {
+    "NaN": "javascript-nan",
+    "undefined": "javascript-undef",
+    "Infinity": "javascript-inf"
+};
+
+WebInspector.SourceJavaScriptTokenizer.prototype = {
+    createInitialCondition: function()
+    {
+        return { lexCondition: this._lexConditions.NODIV };
+    },
+
+    nextToken: function(cursor)
+    {
+        var cursorOnEnter = cursor;
+        var gotoCase = 1;
+        var YYMARKER;
+        while (1) {
+            switch (gotoCase)
+            // Following comment is replaced with generated state machine.
+            /*!re2c
+                re2c:define:YYCTYPE  = "var";
+                re2c:define:YYCURSOR = cursor;
+                re2c:define:YYGETCONDITION = "this.getLexCondition";
+                re2c:define:YYSETCONDITION = "this.setLexCondition";
+                re2c:condprefix = "case this.case_";
+                re2c:condenumprefix = "this._lexConditions.";
+                re2c:yyfill:enable = 0;
+                re2c:labelprefix = "case ";
+                re2c:indent:top = 2;
+                re2c:indent:string = "    ";
+
+                LineComment = "//" [^\r\n]*;
+                CommentContent = ([^*\r\n] | ("*"+[^/*]))*;
+                Comment = "/*" CommentContent "*"+ "/";
+                CommentStart = "/*" CommentContent [\r\n];
+                CommentEnd = CommentContent "*"+ "/";
+
+                DecimalDigit = [0-9];
+                NonZeroDigit = [1-9];
+                OctalDigit = [0-7];
+                HexDigit = [0-9a-fA-F];
+                SignedInteger = ("+"|"-")? DecimalDigit+;
+                ExponentPart = ("e" | "E") SignedInteger;
+                DecimalIntegerLiteral = "0" | NonZeroDigit DecimalDigit*;
+                DecimalLiteral = DecimalIntegerLiteral "." DecimalDigit* ExponentPart? | "." DecimalDigit+ ExponentPart? | DecimalIntegerLiteral ExponentPart?;
+                HexIntegerLiteral = "0" ("x"|"X") HexDigit+;
+                OctalIntegerLiteral = "0" OctalDigit+;
+                NumericLiteral = DecimalLiteral | HexIntegerLiteral | OctalIntegerLiteral;
+
+                Punctuation = [\!\%\&\(\*\+\,\-\.\:\;\<\=\>\?\[\]\^\{\|\}\~] | "!=" | "!==" | "%=" | "&&" | "&=" | "*=" | "++" | "+=" | "--" | "-=" | "<<" | "<<="  | "<=" | "==" | "===" | ">=" | ">>" | ">>=" | ">>>" | ">>>=" | "^=" | "|=" | "||";
+                Division = "/" | "/=";
+                RightParen = ")";
+
+                Letter = [a-zA-Z\x80-\xFF];
+                UnicodeEscapeSequence = "\\u" HexDigit HexDigit HexDigit HexDigit;
+
+                IdentifierStart = Letter | "_" | "$" | UnicodeEscapeSequence;
+                IdentifierPart = IdentifierStart | DecimalDigit;
+                Identifier = IdentifierStart IdentifierPart *;
+                Spaces = " "+;
+
+                DoubleStringContent = ([^\r\n\"\\] | UnicodeEscapeSequence | "\\" ['"\\bfnrtv])*;
+                SingleStringContent = ([^\r\n\'\\] | UnicodeEscapeSequence | "\\" ['"\\bfnrtv])*;
+                StringLiteral = "\"" DoubleStringContent "\"" | "'" SingleStringContent "'";
+                DoubleStringStart = "\"" DoubleStringContent "\\" [\r\n];
+                DoubleStringEnd = DoubleStringContent "\"";
+                SingleStringStart = "'" SingleStringContent "\\" [\r\n];
+                SingleStringEnd = SingleStringContent "'";
+
+                BackslashSequence = "\\" [^\r\n];
+                RegexSet = "[" ([^\r\n*\\/] | BackslashSequence)* "]";
+                RegexFirstChar = [^\r\n*\\/\[\]] | BackslashSequence | RegexSet;
+                RegexChar = [^\r\n\\/\[\]] | BackslashSequence | RegexSet;
+                RegexContent = RegexChar*;
+                Regex = "/" RegexFirstChar RegexContent "/" [igm]*;
+                RegexStart = "/" RegexFirstChar RegexContent "\\";
+                RegexEnd = RegexContent "/" [igm]*;
+
+                <DIV,NODIV> LineComment { this.tokenType = "javascript-comment"; return cursor; }
+                <DIV,NODIV> Comment { this.tokenType = "javascript-comment"; return cursor; }
+                <DIV,NODIV> CommentStart => COMMENT { this.tokenType = "javascript-comment"; return cursor; }
+                <COMMENT> CommentContent => COMMENT { this.tokenType = "javascript-comment"; return cursor; }
+                <COMMENT> CommentEnd => NODIV { this.tokenType = "javascript-comment"; return cursor; }
+
+                <DIV,NODIV> Spaces {this.tokenType = "whitespace"; return cursor; }
+                <DIV,NODIV> StringLiteral { this.tokenType = "javascript-string"; return cursor; }
+                <DIV,NODIV> DoubleStringStart => DSTRING { this.tokenType = "javascript-string"; return cursor; }
+                <DSTRING> DoubleStringContent => DSTRING { this.tokenType = "javascript-string"; return cursor; }
+                <DSTRING> DoubleStringEnd => NODIV { this.tokenType = "javascript-string"; return cursor; }
+                <DIV,NODIV> SingleStringStart => SSTRING { this.tokenType = "javascript-string"; return cursor; }
+                <SSTRING> SingleStringContent => SSTRING { this.tokenType = "javascript-string"; return cursor; }
+                <SSTRING> SingleStringEnd => NODIV { this.tokenType = "javascript-string"; return cursor; }
+
+                <NODIV> Regex { this.tokenType = "javascript-regexp"; return cursor; }
+                <NODIV> RegexStart => REGEX { this.tokenType = "javascript-regexp"; return cursor; }
+                <REGEX> RegexContent => REGEX { this.tokenType = "javascript-regexp"; return cursor; }
+                <REGEX> RegexEnd => NODIV { this.tokenType = "javascript-regexp"; return cursor; }
+
+                <DIV,NODIV> NumericLiteral => DIV { this.tokenType = "javascript-number"; return cursor; }
+                <DIV,NODIV> Identifier => DIV
+                {
+                    var token = this._line.substring(cursorOnEnter, cursor);
+                    if (WebInspector.SourceJavaScriptTokenizer.GlobalObjectValueProperties.hasOwnProperty(token))
+                        this.tokenType = WebInspector.SourceJavaScriptTokenizer.GlobalObjectValueProperties[token];
+                    else if (WebInspector.SourceJavaScriptTokenizer.Keywords[token] === true && token !== "__proto__")
+                        this.tokenType = "javascript-keyword";
+                    else
+                        this.tokenType = "javascript-ident";
+                    return cursor;
+                }
+                <DIV,NODIV> RightParen => DIV { this.tokenType = "brace-end"; return cursor; }
+                <DIV,NODIV> Punctuation => NODIV
+                {
+                    var token = this._line.charAt(cursorOnEnter);
+                    if (token === "{")
+                        this.tokenType = "block-start";
+                    else if (token === "}")
+                        this.tokenType = "block-end";
+                    else if (token === "(")
+                        this.tokenType = "brace-start";
+                    else this.tokenType = null;
+                    return cursor;
+                }
+                <DIV> Division => NODIV { this.tokenType = null; return cursor; }
+                <*> [^] { this.tokenType = null; return cursor; }
+            */
+        }
+    },
+
+    __proto__: WebInspector.SourceTokenizer.prototype
+}
diff --git a/Source/devtools/front_end/SourceMap.js b/Source/devtools/front_end/SourceMap.js
new file mode 100644
index 0000000..4615308
--- /dev/null
+++ b/Source/devtools/front_end/SourceMap.js
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * Implements Source Map V3 model. See http://code.google.com/p/closure-compiler/wiki/SourceMaps
+ * for format description.
+ * @constructor
+ * @param {string} sourceMappingURL
+ * @param {SourceMapV3} payload
+ */
+WebInspector.SourceMap = function(sourceMappingURL, payload)
+{
+    if (!WebInspector.SourceMap.prototype._base64Map) {
+        const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+        WebInspector.SourceMap.prototype._base64Map = {};
+        for (var i = 0; i < base64Digits.length; ++i)
+            WebInspector.SourceMap.prototype._base64Map[base64Digits.charAt(i)] = i;
+    }
+
+    this._sourceMappingURL = sourceMappingURL;
+    this._reverseMappingsBySourceURL = {};
+    this._mappings = [];
+    this._sources = {};
+    this._sourceContentByURL = {};
+    this._parseMappingPayload(payload);
+}
+
+/**
+ * @param {string} sourceMapURL
+ * @param {string} compiledURL
+ * @return {WebInspector.SourceMap}
+ */
+WebInspector.SourceMap.load = function(sourceMapURL, compiledURL)
+{
+    try {
+        // FIXME: make sendRequest async.
+        var response = InspectorFrontendHost.loadResourceSynchronously(sourceMapURL);
+        if (!response)
+            return null;
+        if (response.slice(0, 3) === ")]}")
+            response = response.substring(response.indexOf('\n'));
+        var payload = /** @type {SourceMapV3} */ (JSON.parse(response));
+        var baseURL = sourceMapURL.startsWith("data:") ? compiledURL : sourceMapURL;
+        return new WebInspector.SourceMap(baseURL, payload);
+    } catch(e) {
+        console.error(e.message);
+        return null;
+    }
+}
+
+WebInspector.SourceMap.prototype = {
+    /**
+     * @return {Array.<string>}
+     */
+    sources: function()
+    {
+        return Object.keys(this._sources);
+    },
+
+    /**
+     * @param {string} sourceURL
+     * @return {string|undefined}
+     */
+    sourceContent: function(sourceURL)
+    {
+        return this._sourceContentByURL[sourceURL];
+    },
+
+    /**
+     * @param {SourceMapV3} mappingPayload
+     */
+    _parseMappingPayload: function(mappingPayload)
+    {
+        if (mappingPayload.sections)
+            this._parseSections(mappingPayload.sections);
+        else
+            this._parseMap(mappingPayload, 0, 0);
+    },
+
+    /**
+     * @param {Array.<SourceMapV3.Section>} sections
+     */
+    _parseSections: function(sections)
+    {
+        for (var i = 0; i < sections.length; ++i) {
+            var section = sections[i];
+            this._parseMap(section.map, section.offset.line, section.offset.column);
+        }
+    },
+
+    /**
+     * @param {number} lineNumber in compiled resource
+     * @param {number} columnNumber in compiled resource
+     * @return {?Array}
+     */
+    findEntry: function(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;
+    },
+
+    /**
+     * @param {string} sourceURL of the originating resource
+     * @param {number} lineNumber in the originating resource
+     * @return {Array}
+     */
+    findEntryReversed: function(sourceURL, lineNumber)
+    {
+        var mappings = this._reverseMappingsBySourceURL[sourceURL];
+        for ( ; lineNumber < mappings.length; ++lineNumber) {
+            var mapping = mappings[lineNumber];
+            if (mapping)
+                return mapping;
+        }
+        return this._mappings[0];
+    },
+
+    /**
+     * @override
+     */
+    _parseMap: function(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 sourceRoot = map.sourceRoot || "";
+            if (sourceRoot && !sourceRoot.endsWith("/"))
+                sourceRoot += "/";
+            var href = sourceRoot + originalSourceURL;
+            var url = WebInspector.ParsedURL.completeURL(this._sourceMappingURL, href) || href;
+            originalToCanonicalURLMap[originalSourceURL] = url;
+            sources.push(url);
+            this._sources[url] = true;
+
+            if (map.sourcesContent && map.sourcesContent[i])
+                this._sourceContentByURL[url] = map.sourcesContent[i];
+        }
+
+        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]];
+        }
+    },
+
+    /**
+     * @param {string} char
+     * @return {boolean}
+     */
+    _isSeparator: function(char)
+    {
+        return char === "," || char === ";";
+    },
+
+    /**
+     * @param {WebInspector.SourceMap.StringCharIterator} stringCharIterator
+     * @return {number}
+     */
+    _decodeVLQ: function(stringCharIterator)
+    {
+        // Read unsigned value.
+        var result = 0;
+        var shift = 0;
+        do {
+            var digit = this._base64Map[stringCharIterator.next()];
+            result += (digit & this._VLQ_BASE_MASK) << shift;
+            shift += this._VLQ_BASE_SHIFT;
+        } while (digit & this._VLQ_CONTINUATION_MASK);
+
+        // Fix the sign.
+        var negative = result & 1;
+        result >>= 1;
+        return negative ? -result : result;
+    },
+
+    _VLQ_BASE_SHIFT: 5,
+    _VLQ_BASE_MASK: (1 << 5) - 1,
+    _VLQ_CONTINUATION_MASK: 1 << 5
+}
+
+/**
+ * @constructor
+ * @param {string} string
+ */
+WebInspector.SourceMap.StringCharIterator = function(string)
+{
+    this._string = string;
+    this._position = 0;
+}
+
+WebInspector.SourceMap.StringCharIterator.prototype = {
+    /**
+     * @return {string}
+     */
+    next: function()
+    {
+        return this._string.charAt(this._position++);
+    },
+
+    /**
+     * @return {string}
+     */
+    peek: function()
+    {
+        return this._string.charAt(this._position);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    hasNext: function()
+    {
+        return this._position < this._string.length;
+    }
+}
diff --git a/Source/devtools/front_end/SourceMapping.js b/Source/devtools/front_end/SourceMapping.js
new file mode 100644
index 0000000..a71af9f
--- /dev/null
+++ b/Source/devtools/front_end/SourceMapping.js
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.SourceMapping = function()
+{
+}
+
+WebInspector.SourceMapping.prototype = {
+    /**
+     * @param {WebInspector.RawLocation} rawLocation
+     * @return {WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(rawLocation) { },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.RawLocation}
+     */
+    uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber) { },
+
+    /**
+     * @return {boolean}
+     */
+    isIdentity: function() { }
+}
+
+/**
+ * @interface
+ * @extends {WebInspector.SourceMapping}
+ */
+WebInspector.ScriptSourceMapping = function()
+{
+}
+
+WebInspector.ScriptSourceMapping.prototype = {
+    /**
+     * @param {WebInspector.Script} script
+     */
+    addScript: function(script) { }
+}
\ No newline at end of file
diff --git a/Source/devtools/front_end/SourceTokenizer.js b/Source/devtools/front_end/SourceTokenizer.js
new file mode 100644
index 0000000..8bc3db2
--- /dev/null
+++ b/Source/devtools/front_end/SourceTokenizer.js
@@ -0,0 +1,128 @@
+/* Generated by re2c 0.13.5 on Tue Jan 26 01:16:33 2010 */
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.SourceTokenizer = function()
+{
+    /** @type {?string} */
+    this.tokenType = null;
+}
+
+WebInspector.SourceTokenizer.prototype = {
+    set line(line) {
+        this._line = line;
+    },
+
+    set condition(condition)
+    {
+        this._condition = condition;
+    },
+
+    get condition()
+    {
+        return this._condition;
+    },
+
+    getLexCondition: function()
+    {
+        return this.condition.lexCondition;
+    },
+
+    setLexCondition: function(lexCondition)
+    {
+        this.condition.lexCondition = lexCondition;
+    },
+
+    /**
+     * @param {number} cursor
+     * @return {string}
+     */
+    _charAt: function(cursor)
+    {
+        return cursor < this._line.length ? this._line.charAt(cursor) : "\n";
+    },
+
+    createInitialCondition: function()
+    {
+    },
+
+    /**
+     * @param {number} cursor
+     * @return {number}
+     */
+    nextToken: function(cursor)
+    {
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.SourceTokenizer.Registry = function() {
+    this._tokenizers = {};
+    this._tokenizerConstructors = {
+        "text/css": "SourceCSSTokenizer",
+        "text/html": "SourceHTMLTokenizer",
+        "text/javascript": "SourceJavaScriptTokenizer",
+        "text/x-scss": "SourceCSSTokenizer"
+    };
+}
+
+/**
+ * @return {WebInspector.SourceTokenizer.Registry}
+ */
+WebInspector.SourceTokenizer.Registry.getInstance = function()
+{
+    if (!WebInspector.SourceTokenizer.Registry._instance)
+        WebInspector.SourceTokenizer.Registry._instance = new WebInspector.SourceTokenizer.Registry();
+    return WebInspector.SourceTokenizer.Registry._instance;
+}
+
+WebInspector.SourceTokenizer.Registry.prototype = {
+    /**
+     * @param {string} mimeType
+     * @return {WebInspector.SourceTokenizer}
+     */
+    getTokenizer: function(mimeType)
+    {
+        if (!this._tokenizerConstructors[mimeType])
+            return null;
+        var tokenizerClass = this._tokenizerConstructors[mimeType];
+        var tokenizer = this._tokenizers[tokenizerClass];
+        if (!tokenizer) {
+            tokenizer = new WebInspector[tokenizerClass]();
+            this._tokenizers[tokenizerClass] = tokenizer;
+        }
+        return tokenizer;
+    }
+}
diff --git a/Source/devtools/front_end/Spectrum.js b/Source/devtools/front_end/Spectrum.js
new file mode 100644
index 0000000..a8161df
--- /dev/null
+++ b/Source/devtools/front_end/Spectrum.js
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2011 Brian Grinstead 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.Spectrum = function()
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("spectrum.css");
+
+    this.element.className = "spectrum-container";
+    this.element.tabIndex = 0;
+
+    var topElement = this.element.createChild("div", "spectrum-top");
+    topElement.createChild("div", "spectrum-fill");
+
+    var topInnerElement = topElement.createChild("div", "spectrum-top-inner fill");
+    this._draggerElement = topInnerElement.createChild("div", "spectrum-color");
+    this._dragHelperElement = this._draggerElement.createChild("div", "spectrum-sat fill").createChild("div", "spectrum-val fill").createChild("div", "spectrum-dragger");
+
+    this._sliderElement = topInnerElement.createChild("div", "spectrum-hue");
+    this.slideHelper = this._sliderElement.createChild("div", "spectrum-slider");
+
+    var rangeContainer = this.element.createChild("div", "spectrum-range-container");
+    var alphaLabel = rangeContainer.createChild("label");
+    alphaLabel.textContent = WebInspector.UIString("\u03B1:");
+
+    this._alphaElement = rangeContainer.createChild("input", "spectrum-range");
+    this._alphaElement.setAttribute("type", "range");
+    this._alphaElement.setAttribute("min", "0");
+    this._alphaElement.setAttribute("max", "100");
+    this._alphaElement.addEventListener("change", alphaDrag.bind(this), false);
+
+    var swatchElement = document.createElement("span");
+    swatchElement.className = "swatch";
+    this._swatchInnerElement = swatchElement.createChild("span", "swatch-inner");
+
+    var displayContainer = this.element.createChild("div");
+    displayContainer.appendChild(swatchElement);
+    this._displayElement = displayContainer.createChild("span", "source-code spectrum-display-value");
+
+    WebInspector.Spectrum.draggable(this._sliderElement, hueDrag.bind(this));
+    WebInspector.Spectrum.draggable(this._draggerElement, colorDrag.bind(this), colorDragStart.bind(this));
+
+    function hueDrag(element, dragX, dragY)
+    {
+        this._hsv[0] = dragY / this.slideHeight;
+
+        this._onchange();
+    }
+
+    var initialHelperOffset;
+
+    function colorDragStart(element, dragX, dragY)
+    {
+        initialHelperOffset = { x: this._dragHelperElement.offsetLeft, y: this._dragHelperElement.offsetTop };
+    }
+
+    function colorDrag(element, dragX, dragY, event)
+    {
+        if (event.shiftKey) {
+            if (Math.abs(dragX - initialHelperOffset.x) >= Math.abs(dragY - initialHelperOffset.y))
+                dragY = initialHelperOffset.y;
+            else
+                dragX = initialHelperOffset.x;
+        }
+
+        this._hsv[1] = dragX / this.dragWidth;
+        this._hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
+
+        this._onchange();
+    }
+
+    function alphaDrag()
+    {
+        this._hsv[3] = this._alphaElement.value / 100;
+
+        this._onchange();
+    }
+};
+
+WebInspector.Spectrum.Events = {
+    ColorChanged: "ColorChanged"
+};
+
+/**
+ * @param {Function=} onmove
+ * @param {Function=} onstart
+ * @param {Function=} onstop
+ */
+WebInspector.Spectrum.draggable = function(element, onmove, onstart, onstop) {
+
+    var doc = document;
+    var dragging;
+    var offset;
+    var scrollOffset;
+    var maxHeight;
+    var maxWidth;
+
+    function consume(e)
+    {
+        e.consume(true);
+    }
+
+    function move(e)
+    {
+        if (dragging) {
+            var dragX = Math.max(0, Math.min(e.pageX - offset.left + scrollOffset.left, maxWidth));
+            var dragY = Math.max(0, Math.min(e.pageY - offset.top + scrollOffset.top, maxHeight));
+
+            if (onmove)
+                onmove(element, dragX, dragY, e);
+        }
+    }
+
+    function start(e)
+    {
+        var rightClick = e.which ? (e.which === 3) : (e.button === 2);
+
+        if (!rightClick && !dragging) {
+
+            if (onstart)
+                onstart(element, e)
+
+            dragging = true;
+            maxHeight = element.clientHeight;
+            maxWidth = element.clientWidth;
+
+            scrollOffset = element.scrollOffset();
+            offset = element.totalOffset();
+
+            doc.addEventListener("selectstart", consume, false);
+            doc.addEventListener("dragstart", consume, false);
+            doc.addEventListener("mousemove", move, false);
+            doc.addEventListener("mouseup", stop, false);
+
+            move(e);
+            consume(e);
+        }
+    }
+
+    function stop(e)
+    {
+        if (dragging) {
+            doc.removeEventListener("selectstart", consume, false);
+            doc.removeEventListener("dragstart", consume, false);
+            doc.removeEventListener("mousemove", move, false);
+            doc.removeEventListener("mouseup", stop, false);
+
+            if (onstop)
+                onstop(element, e);
+        }
+
+        dragging = false;
+    }
+
+    element.addEventListener("mousedown", start, false);
+};
+
+WebInspector.Spectrum.prototype = {
+    /**
+     * @param {WebInspector.Color} color
+     */
+    setColor: function(color)
+    {
+        this._hsv = color.hsva();
+    },
+
+    /**
+     * @return {WebInspector.Color}
+     */
+    color: function()
+    {
+        return WebInspector.Color.fromHSVA(this._hsv);
+    },
+
+    _colorString: function()
+    {
+        var cf = WebInspector.Color.Format;
+        var format = this._originalFormat;
+        var color = this.color();
+        var originalFormatString = color.toString(this._originalFormat);
+        if (originalFormatString)
+            return originalFormatString;
+
+        if (color.hasAlpha()) {
+            // Everything except HSL(A) should be returned as RGBA if transparency is involved.
+            if (format === cf.HSLA || format === cf.HSL)
+                return color.toString(cf.HSLA);
+            else
+                return color.toString(cf.RGBA);
+        }
+
+        if (format === cf.ShortHEX)
+            return color.toString(cf.HEX);
+        console.assert(format === cf.Nickname);
+        return color.toString(cf.RGB);
+    },
+
+
+    set displayText(text)
+    {
+        this._displayElement.textContent = text;
+    },
+
+    _onchange: function()
+    {
+        this._updateUI();
+        this.dispatchEventToListeners(WebInspector.Spectrum.Events.ColorChanged, this._colorString());
+    },
+
+    _updateHelperLocations: function()
+    {
+        var h = this._hsv[0];
+        var s = this._hsv[1];
+        var v = this._hsv[2];
+
+        // Where to show the little circle that displays your current selected color.
+        var dragX = s * this.dragWidth;
+        var dragY = this.dragHeight - (v * this.dragHeight);
+
+        dragX = Math.max(-this._dragHelperElementHeight,
+                        Math.min(this.dragWidth - this._dragHelperElementHeight, dragX - this._dragHelperElementHeight));
+        dragY = Math.max(-this._dragHelperElementHeight,
+                        Math.min(this.dragHeight - this._dragHelperElementHeight, dragY - this._dragHelperElementHeight));
+
+        this._dragHelperElement.positionAt(dragX, dragY);
+
+        // Where to show the bar that displays your current selected hue.
+        var slideY = (h * this.slideHeight) - this.slideHelperHeight;
+        this.slideHelper.style.top = slideY + "px";
+
+        this._alphaElement.value = this._hsv[3] * 100;
+    },
+
+    _updateUI: function()
+    {
+        this._updateHelperLocations();
+
+        this._draggerElement.style.backgroundColor = WebInspector.Color.fromHSVA([this._hsv[0], 1, 1, 1]).toString(WebInspector.Color.Format.RGB);
+        this._swatchInnerElement.style.backgroundColor = this.color().toString(WebInspector.Color.Format.RGB);
+
+        this._alphaElement.value = this._hsv[3] * 100;
+    },
+
+    wasShown: function()
+    {
+        this.slideHeight = this._sliderElement.offsetHeight;
+        this.dragWidth = this._draggerElement.offsetWidth;
+        this.dragHeight = this._draggerElement.offsetHeight;
+        this._dragHelperElementHeight = this._dragHelperElement.offsetHeight / 2;
+        this.slideHelperHeight = this.slideHelper.offsetHeight / 2;
+        this._updateUI();
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.SpectrumPopupHelper = function()
+{
+    this._spectrum = new WebInspector.Spectrum();
+    this._spectrum.element.addEventListener("keydown", this._onKeyDown.bind(this), false);
+
+    this._popover = new WebInspector.Popover();
+    this._popover.setCanShrink(false);
+    this._popover.element.addEventListener("mousedown", consumeEvent, false);
+
+    this._hideProxy = this.hide.bind(this, true);
+}
+
+WebInspector.SpectrumPopupHelper.Events = {
+    Hidden: "Hidden"
+};
+
+WebInspector.SpectrumPopupHelper.prototype = {
+    /**
+     * @return {WebInspector.Spectrum}
+     */
+    spectrum: function()
+    {
+        return this._spectrum;
+    },
+
+    toggle: function(element, color, format)
+    {
+        if (this._popover.isShowing())
+            this.hide(true);
+        else
+            this.show(element, color, format);
+
+        return this._popover.isShowing();
+    },
+
+    show: function(element, color, format)
+    {
+        if (this._popover.isShowing()) {
+            if (this._anchorElement === element)
+                return false;
+
+            // Reopen the picker for another anchor element.
+            this.hide(true);
+        }
+
+        this._anchorElement = element;
+
+        this._spectrum.setColor(color);
+        this._spectrum._originalFormat = format !== WebInspector.Color.Format.Original ? format : color.format();
+        this.reposition(element);
+
+        document.addEventListener("mousedown", this._hideProxy, false);
+        window.addEventListener("blur", this._hideProxy, false);
+        return true;
+    },
+
+    reposition: function(element)
+    {
+        if (!this._previousFocusElement)
+            this._previousFocusElement = WebInspector.currentFocusElement();
+        this._popover.showView(this._spectrum, element);
+        WebInspector.setCurrentFocusElement(this._spectrum.element);
+    },
+
+    /**
+     * @param {boolean=} commitEdit
+     */
+    hide: function(commitEdit)
+    {
+        if (!this._popover.isShowing())
+            return;
+        this._popover.hide();
+
+        document.removeEventListener("mousedown", this._hideProxy, false);
+        window.removeEventListener("blur", this._hideProxy, false);
+
+        this.dispatchEventToListeners(WebInspector.SpectrumPopupHelper.Events.Hidden, !!commitEdit);
+
+        WebInspector.setCurrentFocusElement(this._previousFocusElement);
+        delete this._previousFocusElement;
+
+        delete this._anchorElement;
+    },
+
+    _onKeyDown: function(event)
+    {
+        if (event.keyIdentifier === "Enter") {
+            this.hide(true);
+            event.consume(true);
+            return;
+        }
+        if (event.keyIdentifier === "U+001B") { // Escape key
+            this.hide(false);
+            event.consume(true);
+        }
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ */
+WebInspector.ColorSwatch = function()
+{
+    this.element = document.createElement("span");
+    this._swatchInnerElement = this.element.createChild("span", "swatch-inner");
+    this.element.title = WebInspector.UIString("Click to open a colorpicker. Shift-click to change color format");
+    this.element.className = "swatch";
+    this.element.addEventListener("mousedown", consumeEvent, false);
+    this.element.addEventListener("dblclick", consumeEvent, false);
+}
+
+WebInspector.ColorSwatch.prototype = {
+    /**
+     * @param {string} colorString
+     */
+    setColorString: function(colorString)
+    {
+        this._swatchInnerElement.style.backgroundColor = colorString;
+    }
+}
diff --git a/Source/devtools/front_end/SplitView.js b/Source/devtools/front_end/SplitView.js
new file mode 100644
index 0000000..13e50c7
--- /dev/null
+++ b/Source/devtools/front_end/SplitView.js
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {boolean} isVertical
+ * @param {string=} sidebarSizeSettingName
+ * @param {number=} defaultSidebarWidth
+ * @param {number=} defaultSidebarHeight
+ */
+WebInspector.SplitView = function(isVertical, sidebarSizeSettingName, defaultSidebarWidth, defaultSidebarHeight)
+{
+    WebInspector.View.call(this);
+
+    this.registerRequiredCSS("splitView.css");
+
+    this.element.className = "split-view";
+
+    this._firstElement = this.element.createChild("div", "split-view-contents scroll-target split-view-contents-first");
+    this._secondElement = this.element.createChild("div", "split-view-contents scroll-target split-view-contents-second");
+
+    this._resizerElement = this.element.createChild("div", "split-view-resizer");
+    this.installResizer(this._resizerElement);
+    this._resizable = true;
+
+    this._savedSidebarWidth = defaultSidebarWidth || 200;
+    this._savedSidebarHeight = defaultSidebarHeight || this._savedSidebarWidth;
+
+    if (0 < this._savedSidebarWidth && this._savedSidebarWidth < 1 &&
+        0 < this._savedSidebarHeight && this._savedSidebarHeight < 1)
+        this._useFraction = true;
+    
+    this._sidebarSizeSettingName = sidebarSizeSettingName;
+
+    this.setSecondIsSidebar(true);
+
+    this._innerSetVertical(isVertical);
+}
+
+WebInspector.SplitView.prototype = {
+    /**
+     * @return {boolean}
+     */
+    isVertical: function()
+    {
+        return this._isVertical;
+    },
+
+    /**
+     * @param {boolean} isVertical
+     */
+    setVertical: function(isVertical)
+    {
+        if (this._isVertical === isVertical)
+            return;
+
+        this._innerSetVertical(isVertical);
+
+        if (this.isShowing())
+            this._updateLayout();
+    },
+
+    /**
+     * @param {boolean} isVertical
+     */
+    _innerSetVertical: function(isVertical)
+    {
+        this.element.removeStyleClass(this._isVertical ? "split-view-vertical" : "split-view-horizontal");
+        this._isVertical = isVertical;
+        this.element.addStyleClass(this._isVertical ? "split-view-vertical" : "split-view-horizontal");
+    },
+  
+    _updateLayout: function()
+    {
+        this._updateTotalSize();
+
+        delete this._sidebarSize;  // Force update.
+        this.setSidebarSize(this._lastSidebarSize());
+    },
+
+    /**
+     * @return {Element}
+     */
+    firstElement: function()
+    {
+        return this._firstElement;
+    },
+
+    /**
+     * @return {Element}
+     */
+    secondElement: function()
+    {
+        return this._secondElement;
+    },
+
+    /**
+     * @return {Element}
+     */
+    get mainElement()
+    {
+        return this.isSidebarSecond() ? this.firstElement() : this.secondElement();
+    },
+
+    /**
+     * @return {Element}
+     */
+    get sidebarElement()
+    {
+        return this.isSidebarSecond() ? this.secondElement() : this.firstElement();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isSidebarSecond: function()
+    {
+        return this._secondIsSidebar;
+    },
+
+    /**
+     * @param {boolean} secondIsSidebar
+     */
+    setSecondIsSidebar: function(secondIsSidebar)
+    {
+        this.sidebarElement.removeStyleClass("split-view-sidebar");
+        this._secondIsSidebar = secondIsSidebar;
+        this.sidebarElement.addStyleClass("split-view-sidebar");
+    },
+
+    /**
+     * @return {Element}
+     */
+    resizerElement: function()
+    {
+        return this._resizerElement;
+    },
+
+    showOnlyFirst: function()
+    {
+        this._showOnly(this._firstElement, this._secondElement);
+    },
+
+    showOnlySecond: function()
+    {
+        this._showOnly(this._secondElement, this._firstElement);
+    },
+
+    /**
+     * @param {Element} sideA
+     * @param {Element} sideB
+     */
+    _showOnly: function(sideA, sideB)
+    {
+        sideA.removeStyleClass("hidden");
+        sideA.addStyleClass("maximized");
+        sideB.addStyleClass("hidden");
+        sideB.removeStyleClass("maximized");
+        this._removeAllLayoutProperties();
+
+        this._isShowingOne = true;
+        this.setResizable(false);
+        this.doResize();
+    },
+
+    _removeAllLayoutProperties: function()
+    {
+        this._firstElement.style.removeProperty("right");
+        this._firstElement.style.removeProperty("bottom");
+        this._firstElement.style.removeProperty("width");
+        this._firstElement.style.removeProperty("height");
+
+        this._secondElement.style.removeProperty("left");
+        this._secondElement.style.removeProperty("top");
+        this._secondElement.style.removeProperty("width");
+        this._secondElement.style.removeProperty("height");
+
+        this._resizerElement.style.removeProperty("left");
+        this._resizerElement.style.removeProperty("right");
+        this._resizerElement.style.removeProperty("top");
+        this._resizerElement.style.removeProperty("bottom");
+
+        this._resizerElement.style.removeProperty("margin-left");
+        this._resizerElement.style.removeProperty("margin-right");
+        this._resizerElement.style.removeProperty("margin-top");
+        this._resizerElement.style.removeProperty("margin-bottom");
+    },
+
+    showBoth: function()
+    {
+        this._isShowingOne = false;
+        this._firstElement.removeStyleClass("hidden");
+        this._firstElement.removeStyleClass("maximized");
+        this._secondElement.removeStyleClass("hidden");
+        this._secondElement.removeStyleClass("maximized");
+
+        this._updateLayout();
+
+        this.setResizable(true);
+        this.doResize();
+    },
+
+    /**
+     * @param {boolean} resizable
+     */
+    setResizable: function(resizable)
+    {
+        if (this._resizable === resizable)
+            return;
+        this._resizable = resizable;
+        if (resizable)
+            this._resizerElement.removeStyleClass("hidden");
+        else
+            this._resizerElement.addStyleClass("hidden");
+    },
+
+    /**
+     * @param {number} size
+     */
+    setSidebarSize: function(size)
+    {
+        if (this._sidebarSize === size)
+            return;
+
+        size = this.applyConstraints(size);
+        if (size < 0) {
+            // Never apply bad values, fix it upon onResize instead.
+            this._sidebarSize = size;
+            return;
+        }
+        this._innerSetSidebarSize(size);
+        this._saveSidebarSize(size);
+    },
+
+    /**
+     * @return {number}
+     */
+    sidebarSize: function()
+    {
+        return this._sidebarSize;
+    },
+
+    /**
+     * @return {number}
+     */
+    totalSize: function()
+    {
+        return this._totalSize;
+    },
+
+    _updateTotalSize: function()
+    {
+        this._totalSize = this._isVertical ? this.element.offsetWidth : this.element.offsetHeight;
+        if (this._useFraction)
+            this._sidebarSize = this._lastSidebarSize();
+    },
+
+    /**
+     * @param {number} size
+     */
+    _innerSetSidebarSize: function(size)
+    {
+        if (this._isShowingOne)
+            return;
+
+        this._removeAllLayoutProperties();
+
+        var sizeValue;
+        if (this._useFraction)
+            sizeValue = (size / this._totalSize) * 100 + "%";
+        else
+            sizeValue = size + "px";
+
+        if (this._isVertical) {
+            var resizerWidth = this._resizerElement.offsetWidth;
+            if (this._secondIsSidebar) {
+                this._firstElement.style.right = sizeValue;
+                this._secondElement.style.width = sizeValue;
+                this._resizerElement.style.right = sizeValue;
+                this._resizerElement.style.marginRight = -resizerWidth / 2 + "px";
+            } else {
+                this._firstElement.style.width = sizeValue;
+                this._secondElement.style.left = sizeValue;
+                this._resizerElement.style.left = sizeValue;
+                this._resizerElement.style.marginLeft = -resizerWidth / 2 + "px";
+            }
+        } else {
+            var resizerHeight = this._resizerElement.offsetHeight;
+
+            if (this._secondIsSidebar) {
+                this._firstElement.style.bottom = sizeValue;
+                this._secondElement.style.height = sizeValue;
+                this._resizerElement.style.bottom = sizeValue;
+                this._resizerElement.style.marginBottom = -resizerHeight / 2 + "px";
+            } else {
+                this._firstElement.style.height = sizeValue;
+                this._secondElement.style.top = sizeValue;
+                this._resizerElement.style.top = sizeValue;
+                this._resizerElement.style.marginTop = -resizerHeight / 2 + "px";
+            }
+        }
+
+        this._sidebarSize = size;
+        this.doResize();
+    },
+
+    /**
+     * @param {number} size
+     * @return {number}
+     */
+    applyConstraints: function(size)
+    {
+        const minSize = 20;
+        size = Math.max(size, minSize);
+        if (this._totalSize - size < minSize)
+            size = this._totalSize - minSize;
+        return size < minSize ? -1 : size;
+    },
+
+    wasShown: function()
+    {
+        this._updateLayout();
+    },
+
+    onResize: function()
+    {
+        if (this._sidebarSize < 0)
+            this._updateLayout();
+        else
+            this._updateTotalSize();
+    },
+
+    /**
+     * @param {Event} event
+     * @return {boolean}
+     */
+    _startResizerDragging: function(event)
+    {
+        if (!this._resizable)
+            return false;
+
+        this._dragOffset = (this._secondIsSidebar ? this._totalSize - this._sidebarSize : this._sidebarSize) - (this._isVertical ? event.pageX : event.pageY);
+        return true;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _resizerDragging: function(event)
+    {
+        var newOffset = (this._isVertical ? event.pageX : event.pageY) + this._dragOffset;
+        var newSize = (this._secondIsSidebar ? this._totalSize - newOffset : newOffset);
+        this.setSidebarSize(newSize);
+        event.preventDefault();
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _endResizerDragging: function(event)
+    {
+        delete this._dragOffset;
+    },
+
+    /**
+     * @param {Element} resizerElement
+     */
+    installResizer: function(resizerElement)
+    {
+        resizerElement.addEventListener("mousedown", this._onDragStart.bind(this), false);
+    },
+
+    /**
+     *
+     * @param {Event} event
+     */
+    _onDragStart: function(event)
+    {
+        WebInspector._elementDragStart(this._startResizerDragging.bind(this), this._resizerDragging.bind(this), this._endResizerDragging.bind(this), this._isVertical ? "ew-resize" : "ns-resize", event);
+    },
+
+    /**
+     * @return {WebInspector.Setting}
+     */
+    _sizeSetting: function()
+    {
+        if (!this._sidebarSizeSettingName)
+            return null;
+
+        var settingName = this._sidebarSizeSettingName + (this._isVertical ? "" : "H");
+        if (!WebInspector.settings[settingName])
+            WebInspector.settings[settingName] = WebInspector.settings.createSetting(settingName, undefined);
+
+        return WebInspector.settings[settingName];
+    },
+
+    /**
+     * @return {number}
+     */
+    _lastSidebarSize: function()
+    {
+        var sizeSetting = this._sizeSetting();
+        var size = sizeSetting ? sizeSetting.get() : 0;
+        if (!size)
+             size = this._isVertical ? this._savedSidebarWidth : this._savedSidebarHeight;
+        if (this._useFraction)
+            size *= this._totalSize;
+        return size;
+    },
+
+    /**
+     * @param {number} size
+     */
+    _saveSidebarSize: function(size)
+    {
+        if (this._useFraction)
+            size /= this._totalSize;
+
+        if (this._isVertical)
+            this._savedSidebarWidth = size;
+        else
+            this._savedSidebarHeight = size;
+
+        var sizeSetting = this._sizeSetting();
+        if (sizeSetting)
+            sizeSetting.set(size);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
diff --git a/Source/devtools/front_end/StatusBarButton.js b/Source/devtools/front_end/StatusBarButton.js
new file mode 100644
index 0000000..074b1f0
--- /dev/null
+++ b/Source/devtools/front_end/StatusBarButton.js
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {!Element} element
+ */
+WebInspector.StatusBarItem = function(element)
+{
+    this.element = element;
+    this._enabled = true;
+}
+
+WebInspector.StatusBarItem.prototype = {
+    /**
+     * @param {boolean} value
+     */
+    setEnabled: function(value)
+    {
+        if (this._enabled === value)
+            return;
+        this._enabled = value;
+        this._applyEnabledState();
+    },
+
+    /**
+     * @protected
+     */
+    _applyEnabledState: function()
+    {
+        this.element.disabled = !this._enabled;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StatusBarItem}
+ * @param {string} text
+ * @param {string=} className
+ */
+WebInspector.StatusBarText = function(text, className)
+{
+    WebInspector.StatusBarItem.call(this, document.createElement("span"));
+    this.element.className = "status-bar-item status-bar-text";
+    if (className)
+        this.element.addStyleClass(className);
+    this.element.textContent = text;
+}
+
+WebInspector.StatusBarText.prototype = {
+    /**
+     * @param {string} text
+     */
+    setText: function(text)
+    {
+        this.element.textContent = text;
+    },
+
+    __proto__: WebInspector.StatusBarItem.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.StatusBarItem}
+ * @param {string} title
+ * @param {string} className
+ * @param {number=} states
+ */
+WebInspector.StatusBarButton = function(title, className, states)
+{
+    WebInspector.StatusBarItem.call(this, document.createElement("button"));
+    this.element.className = className + " status-bar-item";
+    this.element.addEventListener("click", this._clicked.bind(this), false);
+
+    this.glyph = document.createElement("div");
+    this.glyph.className = "glyph";
+    this.element.appendChild(this.glyph);
+
+    this.glyphShadow = document.createElement("div");
+    this.glyphShadow.className = "glyph shadow";
+    this.element.appendChild(this.glyphShadow);
+
+    this.states = states;
+    if (!states)
+        this.states = 2;
+
+    if (states == 2)
+        this._state = false;
+    else
+        this._state = 0;
+
+    this.title = title;
+    this.className = className;
+    this._visible = true;
+}
+
+WebInspector.StatusBarButton.prototype = {
+    _clicked: function()
+    {
+        this.dispatchEventToListeners("click");
+        if (this._longClickInterval)
+            clearInterval(this._longClickInterval);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    enabled: function()
+    {
+        return this._enabled;
+    },
+
+    get title()
+    {
+        return this._title;
+    },
+
+    set title(x)
+    {
+        if (this._title === x)
+            return;
+        this._title = x;
+        this.element.title = x;
+    },
+
+    get state()
+    {
+        return this._state;
+    },
+
+    set state(x)
+    {
+        if (this._state === x)
+            return;
+
+        if (this.states === 2)
+            this.element.enableStyleClass("toggled-on", x);
+        else {
+            this.element.removeStyleClass("toggled-" + this._state);
+            if (x !== 0)
+                this.element.addStyleClass("toggled-" + x);
+        }
+        this._state = x;
+    },
+
+    get toggled()
+    {
+        if (this.states !== 2)
+            throw("Only used toggled when there are 2 states, otherwise, use state");
+        return this.state;
+    },
+
+    set toggled(x)
+    {
+        if (this.states !== 2)
+            throw("Only used toggled when there are 2 states, otherwise, use state");
+        this.state = x;
+    },
+
+    get visible()
+    {
+        return this._visible;
+    },
+
+    set visible(x)
+    {
+        if (this._visible === x)
+            return;
+
+        this.element.enableStyleClass("hidden", !x);
+        this._visible = x;
+    },
+
+    makeLongClickEnabled: function()
+    {
+        this.element.addEventListener("mousedown", mouseDown.bind(this), false);
+        this.element.addEventListener("mouseout", mouseUp.bind(this), false);
+        this.element.addEventListener("mouseup", mouseUp.bind(this), false);
+
+        var longClicks = 0;
+
+        function mouseDown(e)
+        {
+            if (e.which !== 1)
+                return;
+            longClicks = 0;
+            this._longClickInterval = setInterval(longClicked.bind(this), 200);
+        }
+
+        function mouseUp(e)
+        {
+            if (e.which !== 1)
+                return;
+            if (this._longClickInterval)
+                clearInterval(this._longClickInterval);
+        }
+
+        function longClicked()
+        {
+            ++longClicks;
+            this.dispatchEventToListeners(longClicks === 1 ? "longClickDown" : "longClickPress");
+        }
+    },
+
+    /**
+     * @param {function():Array.<WebInspector.StatusBarButton>} buttonsProvider
+     */
+    makeLongClickOptionsEnabled: function(buttonsProvider)
+    {
+        this.makeLongClickEnabled();
+
+        this.longClickGlyph = document.createElement("div");
+        this.longClickGlyph.className = "fill long-click-glyph";
+        this.element.appendChild(this.longClickGlyph);
+
+        this.longClickGlyphShadow = document.createElement("div");
+        this.longClickGlyphShadow.className = "fill long-click-glyph shadow";
+        this.element.appendChild(this.longClickGlyphShadow);
+
+        this.addEventListener("longClickDown", this._showOptions.bind(this, buttonsProvider), this);
+    },
+
+    /**
+     * @param {function():Array.<WebInspector.StatusBarButton>} buttonsProvider
+     */
+    _showOptions: function(buttonsProvider)
+    {
+        var buttons = buttonsProvider();
+        var mainButtonClone = new WebInspector.StatusBarButton(this.title, this.className, this.states);
+        mainButtonClone.addEventListener("click", this._clicked, this);
+        mainButtonClone.state = this.state;
+        buttons.push(mainButtonClone);
+
+        var mouseUpListener = mouseUp.bind(this);
+        document.documentElement.addEventListener("mouseup", mouseUpListener, false);
+
+        var optionsGlassPane = new WebInspector.GlassPane();
+        var optionsBarElement = optionsGlassPane.element.createChild("div", "alternate-status-bar-buttons-bar");
+        const buttonHeight = 24;
+        optionsBarElement.style.height = (buttonHeight * buttons.length) + "px";
+        optionsBarElement.style.left = (this.element.offsetLeft + 1) + "px";
+
+        var boundMouseOver = mouseOver.bind(this);
+        var boundMouseOut = mouseOut.bind(this);
+        for (var i = 0; i < buttons.length; ++i) {
+            buttons[i].element.addEventListener("mousemove", boundMouseOver, false);
+            buttons[i].element.addEventListener("mouseout", boundMouseOut, false);
+            optionsBarElement.appendChild(buttons[i].element);
+        }
+        buttons[buttons.length - 1].element.addStyleClass("emulate-active");
+
+        function mouseOver(e)
+        {
+            if (e.which !== 1)
+                return;
+            var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
+            buttonElement.addStyleClass("emulate-active");
+        }
+
+        function mouseOut(e)
+        {
+            if (e.which !== 1)
+                return;
+            var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
+            buttonElement.removeStyleClass("emulate-active");
+        }
+
+        function mouseUp(e)
+        {
+            if (e.which !== 1)
+                return;
+            optionsGlassPane.dispose();
+            document.documentElement.removeEventListener("mouseup", mouseUpListener, false);
+
+            for (var i = 0; i < buttons.length; ++i) {
+                if (buttons[i].element.hasStyleClass("emulate-active")) {
+                    buttons[i].element.removeStyleClass("emulate-active");
+                    buttons[i]._clicked();
+                    break;
+                }
+            }
+        }
+    },
+
+    __proto__: WebInspector.StatusBarItem.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StatusBarItem}
+ * @param {?function(Event)} changeHandler
+ * @param {string=} className
+ */
+WebInspector.StatusBarComboBox = function(changeHandler, className)
+{
+    WebInspector.StatusBarItem.call(this, document.createElement("span"));
+    this.element.className = "status-bar-select-container";
+
+    this._selectElement = this.element.createChild("select", "status-bar-item");
+    this.element.createChild("div", "status-bar-select-arrow");
+    if (changeHandler)
+        this._selectElement.addEventListener("change", changeHandler, false);
+    if (className)
+        this._selectElement.addStyleClass(className);
+}
+
+WebInspector.StatusBarComboBox.prototype = {
+    /**
+     * @return {number}
+     */
+    size: function()
+    {
+        return this._selectElement.childElementCount;
+    },
+
+    /**
+     * @param {!Element} option
+     */
+    addOption: function(option)
+    {
+        this._selectElement.appendChild(option);
+    },
+
+    /**
+     * @param {string} label
+     * @param {string=} title
+     * @param {string=} value
+     * @return {!Element}
+     */
+    createOption: function(label, title, value)
+    {
+        var option = this._selectElement.createChild("option");
+        option.text = label;
+        if (title)
+            option.title = title;
+        if (typeof value !== "undefined")
+            option.value = value;
+        return option;
+    },
+
+    /**
+     * @override
+     */
+    _applyEnabledState: function()
+    {
+        this._selectElement.disabled = !this._enabled;
+    },
+
+    /**
+     * @param {!Element} option
+     */
+    removeOption: function(option)
+    {
+        this._selectElement.removeChild(option);
+    },
+
+    removeOptions: function()
+    {
+        this._selectElement.removeChildren();
+    },
+
+    /**
+     * @return {?Element}
+     */
+    selectedOption: function()
+    {
+        if (this._selectElement.selectedIndex >= 0)
+            return this._selectElement[this._selectElement.selectedIndex];
+        return null;
+    },
+
+    /**
+     * @param {Element} option
+     */
+    select: function(option)
+    {
+        this._selectElement.selectedIndex = Array.prototype.indexOf.call(this._selectElement, option);
+    },
+
+    /**
+     * @param {number} index
+     */
+    setSelectedIndex: function(index)
+    {
+        this._selectElement.selectedIndex = index;
+    },
+
+    /**
+     * @return {number}
+     */
+    selectedIndex: function()
+    {
+        return this._selectElement.selectedIndex;
+    },
+
+    __proto__: WebInspector.StatusBarItem.prototype
+}
diff --git a/Source/devtools/front_end/StyleSheetOutlineDialog.js b/Source/devtools/front_end/StyleSheetOutlineDialog.js
new file mode 100644
index 0000000..7d25ba3
--- /dev/null
+++ b/Source/devtools/front_end/StyleSheetOutlineDialog.js
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.SelectionDialogContentProvider}
+ * @param {WebInspector.View} view
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ */
+WebInspector.StyleSheetOutlineDialog = function(view, uiSourceCode)
+{
+    WebInspector.SelectionDialogContentProvider.call(this);
+
+    this._rules = [];
+    this._view = view;
+    this._uiSourceCode = uiSourceCode;
+}
+
+/**
+ * @param {WebInspector.View} view
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ */
+WebInspector.StyleSheetOutlineDialog.show = function(view, uiSourceCode)
+{
+    if (WebInspector.Dialog.currentInstance())
+        return null;
+    var delegate = new WebInspector.StyleSheetOutlineDialog(view, uiSourceCode);
+    var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(delegate);
+    WebInspector.Dialog.show(view.element, filteredItemSelectionDialog);
+}
+
+WebInspector.StyleSheetOutlineDialog.prototype = {
+    /**
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemTitleAt: function(itemIndex)
+    {
+        return this._rules[itemIndex].selectorText;
+    },
+
+    /*
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemSuffixAt: function(itemIndex)
+    {
+        return "";
+    },
+
+    /*
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemSubtitleAt: function(itemIndex)
+    {
+        return ":" + (this._rules[itemIndex].sourceLine + 1);
+    },
+
+    /**
+     * @param {number} itemIndex
+     * @return {string}
+     */
+    itemKeyAt: function(itemIndex)
+    {
+        return this._rules[itemIndex].selectorText;
+    },
+
+    /**
+     * @return {number}
+     */
+    itemsCount: function()
+    {
+        return this._rules.length;
+    },
+
+    /**
+     * @param {function(number, number)} callback
+     */
+    requestItems: function(callback)
+    {
+        function didGetAllStyleSheets(error, infos)
+        {
+            if (error) {
+                callback(0, 0);
+                return;
+            }
+  
+            for (var i = 0; i < infos.length; ++i) {
+                var info = infos[i];
+                if (info.sourceURL === this._uiSourceCode.url) {
+                    WebInspector.CSSStyleSheet.createForId(info.styleSheetId, didGetStyleSheet.bind(this));
+                    return;
+                }
+            }
+            callback(0, 0);
+        }
+
+        CSSAgent.getAllStyleSheets(didGetAllStyleSheets.bind(this));
+
+        /**
+         * @param {?WebInspector.CSSStyleSheet} styleSheet
+         */
+        function didGetStyleSheet(styleSheet)
+        {
+            if (!styleSheet) {
+                callback(0, 0);
+                return;
+            }
+
+            this._rules = styleSheet.rules;
+            callback(0, 1);
+        }
+    },
+
+    /**
+     * @param {number} itemIndex
+     * @param {string} promptValue
+     */
+    selectItem: function(itemIndex, promptValue)
+    {
+        var lineNumber = this._rules[itemIndex].sourceLine;
+        if (!isNaN(lineNumber) && lineNumber >= 0)
+            this._view.highlightLine(lineNumber);
+        this._view.focus();
+    },
+
+    /**
+     * @param {string} query
+     * @return {string}
+     */
+    rewriteQuery: function(query)
+    {
+        return query;
+    },
+
+    dispose: function()
+    {
+    }
+}
diff --git a/Source/devtools/front_end/StylesSidebarPane.js b/Source/devtools/front_end/StylesSidebarPane.js
new file mode 100644
index 0000000..28ebee3
--- /dev/null
+++ b/Source/devtools/front_end/StylesSidebarPane.js
@@ -0,0 +1,2738 @@
+/*
+ * Copyright (C) 2007 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 Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ * @param {WebInspector.ComputedStyleSidebarPane} computedStylePane
+ * @param {function(DOMAgent.NodeId, string, boolean)} setPseudoClassCallback
+ */
+WebInspector.StylesSidebarPane = function(computedStylePane, setPseudoClassCallback)
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles"));
+
+    this.settingsSelectElement = document.createElement("select");
+    this.settingsSelectElement.className = "select-settings";
+
+    var option = document.createElement("option");
+    option.value = WebInspector.Color.Format.Original;
+    option.label = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "As authored" : "As Authored");
+    this.settingsSelectElement.appendChild(option);
+
+    option = document.createElement("option");
+    option.value = WebInspector.Color.Format.HEX;
+    option.label = WebInspector.UIString("Hex Colors");
+    this.settingsSelectElement.appendChild(option);
+
+    option = document.createElement("option");
+    option.value = WebInspector.Color.Format.RGB;
+    option.label = WebInspector.UIString("RGB Colors");
+    this.settingsSelectElement.appendChild(option);
+
+    option = document.createElement("option");
+    option.value = WebInspector.Color.Format.HSL;
+    option.label = WebInspector.UIString("HSL Colors");
+    this.settingsSelectElement.appendChild(option);
+
+    // Prevent section from collapsing.
+    var muteEventListener = function(event) { event.consume(true); };
+
+    this.settingsSelectElement.addEventListener("click", muteEventListener, true);
+    this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false);
+    this._updateColorFormatFilter();
+
+    this.titleElement.appendChild(this.settingsSelectElement);
+
+    this._elementStateButton = document.createElement("button");
+    this._elementStateButton.className = "pane-title-button element-state";
+    this._elementStateButton.title = WebInspector.UIString("Toggle Element State");
+    this._elementStateButton.addEventListener("click", this._toggleElementStatePane.bind(this), false);
+    this.titleElement.appendChild(this._elementStateButton);
+
+    var addButton = document.createElement("button");
+    addButton.className = "pane-title-button add";
+    addButton.id = "add-style-button-test-id";
+    addButton.title = WebInspector.UIString("New Style Rule");
+    addButton.addEventListener("click", this._createNewRule.bind(this), false);
+    this.titleElement.appendChild(addButton);
+
+    this._computedStylePane = computedStylePane;
+    computedStylePane._stylesSidebarPane = this;
+    this._setPseudoClassCallback = setPseudoClassCallback;
+    this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
+    WebInspector.settings.colorFormat.addChangeListener(this._colorFormatSettingChanged.bind(this));
+
+    this._createElementStatePane();
+    this.bodyElement.appendChild(this._elementStatePane);
+    this._sectionsContainer = document.createElement("div");
+    this.bodyElement.appendChild(this._sectionsContainer);
+
+    this._spectrumHelper = new WebInspector.SpectrumPopupHelper();
+    this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultCSSFormatter());
+
+    WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetOrMediaQueryResultChanged, this);
+    WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged, this._styleSheetOrMediaQueryResultChanged, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._attributeChanged, this);
+    WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrRemoved, this._attributeChanged, this);
+    WebInspector.settings.showUserAgentStyles.addChangeListener(this._showUserAgentStylesSettingChanged.bind(this));
+}
+
+// Keep in sync with RenderStyleConstants.h PseudoId enum. Array below contains pseudo id names for corresponding enum indexes.
+// First item is empty due to its artificial NOPSEUDO nature in the enum.
+// FIXME: find a way of generating this mapping or getting it from combination of RenderStyleConstants and CSSSelector.cpp at
+// runtime.
+WebInspector.StylesSidebarPane.PseudoIdNames = [
+    "", "first-line", "first-letter", "before", "after", "selection", "", "-webkit-scrollbar", "-webkit-file-upload-button",
+    "-webkit-input-placeholder", "-webkit-slider-thumb", "-webkit-search-cancel-button", "-webkit-search-decoration",
+    "-webkit-search-results-decoration", "-webkit-search-results-button", "-webkit-media-controls-panel",
+    "-webkit-media-controls-play-button", "-webkit-media-controls-mute-button", "-webkit-media-controls-timeline",
+    "-webkit-media-controls-timeline-container", "-webkit-media-controls-volume-slider",
+    "-webkit-media-controls-volume-slider-container", "-webkit-media-controls-current-time-display",
+    "-webkit-media-controls-time-remaining-display", "-webkit-media-controls-seek-back-button", "-webkit-media-controls-seek-forward-button",
+    "-webkit-media-controls-fullscreen-button", "-webkit-media-controls-rewind-button", "-webkit-media-controls-return-to-realtime-button",
+    "-webkit-media-controls-toggle-closed-captions-button", "-webkit-media-controls-status-display", "-webkit-scrollbar-thumb",
+    "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-corner",
+    "-webkit-resizer", "-webkit-inner-spin-button", "-webkit-outer-spin-button"
+];
+
+WebInspector.StylesSidebarPane.canonicalPropertyName = function(name)
+{
+    if (!name || name.length < 9 || name.charAt(0) !== "-")
+        return name;
+    var match = name.match(/(?:-webkit-|-khtml-|-apple-)(.+)/);
+    if (!match)
+        return name;
+    return match[1];
+}
+
+WebInspector.StylesSidebarPane.createExclamationMark = function(propertyName)
+{
+    var exclamationElement = document.createElement("div");
+    exclamationElement.className = "exclamation-mark warning-icon-small";
+    exclamationElement.title = WebInspector.CSSMetadata.cssPropertiesMetainfo.keySet()[propertyName.toLowerCase()] ? WebInspector.UIString("Invalid property value.") : WebInspector.UIString("Unknown property name.");
+    return exclamationElement;
+}
+
+WebInspector.StylesSidebarPane.prototype = {
+    /**
+     * @param {Event} event
+     */
+    _contextMenuEventFired: function(event)
+    {
+        // We start editing upon click -> default navigation to resources panel is not available
+        // Hence we add a soft context menu for hrefs.
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendApplicableItems(event.target);
+        contextMenu.show();
+    },
+
+    get _forcedPseudoClasses()
+    {
+        return this.node ? (this.node.getUserProperty("pseudoState") || undefined) : undefined;
+    },
+
+    _updateForcedPseudoStateInputs: function()
+    {
+        if (!this.node)
+            return;
+
+        var nodePseudoState = this._forcedPseudoClasses;
+        if (!nodePseudoState)
+            nodePseudoState = [];
+
+        var inputs = this._elementStatePane.inputs;
+        for (var i = 0; i < inputs.length; ++i)
+            inputs[i].checked = nodePseudoState.indexOf(inputs[i].state) >= 0;
+    },
+
+    /**
+     * @param {WebInspector.DOMNode=} node
+     * @param {boolean=} forceUpdate
+     */
+    update: function(node, forceUpdate)
+    {
+        this._spectrumHelper.hide();
+
+        var refresh = false;
+
+        if (forceUpdate)
+            delete this.node;
+
+        if (!forceUpdate && (node === this.node))
+            refresh = true;
+
+        if (node && node.nodeType() === Node.TEXT_NODE && node.parentNode)
+            node = node.parentNode;
+
+        if (node && node.nodeType() !== Node.ELEMENT_NODE)
+            node = null;
+
+        if (node)
+            this.node = node;
+        else
+            node = this.node;
+
+        this._updateForcedPseudoStateInputs();
+
+        if (refresh)
+            this._refreshUpdate();
+        else
+            this._rebuildUpdate();
+    },
+
+    /**
+     * @param {WebInspector.StylePropertiesSection=} editedSection
+     * @param {boolean=} forceFetchComputedStyle
+     * @param {function()=} userCallback
+     */
+    _refreshUpdate: function(editedSection, forceFetchComputedStyle, userCallback)
+    {
+        if (this._refreshUpdateInProgress) {
+            this._lastNodeForInnerRefresh = this.node;
+            return;
+        }
+
+        var node = this._validateNode(userCallback);
+        if (!node)
+            return;
+
+        function computedStyleCallback(computedStyle)
+        {
+            delete this._refreshUpdateInProgress;
+
+            if (this._lastNodeForInnerRefresh) {
+                delete this._lastNodeForInnerRefresh;
+                this._refreshUpdate(editedSection, forceFetchComputedStyle, userCallback);
+                return;
+            }
+
+            if (this.node === node && computedStyle)
+                this._innerRefreshUpdate(node, computedStyle, editedSection);
+
+            if (userCallback)
+                userCallback();
+        }
+
+        if (this._computedStylePane.isShowing() || forceFetchComputedStyle) {
+            this._refreshUpdateInProgress = true;
+            WebInspector.cssModel.getComputedStyleAsync(node.id, computedStyleCallback.bind(this));
+        } else {
+            this._innerRefreshUpdate(node, null, editedSection);
+            if (userCallback)
+                userCallback();
+        }
+    },
+
+    _rebuildUpdate: function()
+    {
+        if (this._rebuildUpdateInProgress) {
+            this._lastNodeForInnerRebuild = this.node;
+            return;
+        }
+
+        var node = this._validateNode();
+        if (!node)
+            return;
+
+        this._rebuildUpdateInProgress = true;
+
+        var resultStyles = {};
+
+        function stylesCallback(matchedResult)
+        {
+            delete this._rebuildUpdateInProgress;
+
+            var lastNodeForRebuild = this._lastNodeForInnerRebuild;
+            if (lastNodeForRebuild) {
+                delete this._lastNodeForInnerRebuild;
+                if (lastNodeForRebuild !== this.node) {
+                    this._rebuildUpdate();
+                    return;
+                }
+            }
+
+            if (matchedResult && this.node === node) {
+                resultStyles.matchedCSSRules = matchedResult.matchedCSSRules;
+                resultStyles.pseudoElements = matchedResult.pseudoElements;
+                resultStyles.inherited = matchedResult.inherited;
+                this._innerRebuildUpdate(node, resultStyles);
+            }
+
+            if (lastNodeForRebuild) {
+                // lastNodeForRebuild is the same as this.node - another rebuild has been requested.
+                this._rebuildUpdate();
+                return;
+            }
+        }
+
+        function inlineCallback(inlineStyle, attributesStyle)
+        {
+            resultStyles.inlineStyle = inlineStyle;
+            resultStyles.attributesStyle = attributesStyle;
+        }
+
+        function computedCallback(computedStyle)
+        {
+            resultStyles.computedStyle = computedStyle;
+        }
+
+        if (this._computedStylePane.isShowing())
+            WebInspector.cssModel.getComputedStyleAsync(node.id, computedCallback.bind(this));
+        WebInspector.cssModel.getInlineStylesAsync(node.id, inlineCallback.bind(this));
+        WebInspector.cssModel.getMatchedStylesAsync(node.id, true, true, stylesCallback.bind(this));
+    },
+
+    /**
+     * @param {function()=} userCallback
+     */
+    _validateNode: function(userCallback)
+    {
+        if (!this.node) {
+            this._sectionsContainer.removeChildren();
+            this._computedStylePane.bodyElement.removeChildren();
+            this.sections = {};
+            if (userCallback)
+                userCallback();
+            return null;
+        }
+        return this.node;
+    },
+
+    _styleSheetOrMediaQueryResultChanged: function()
+    {
+        if (this._userOperation || this._isEditingStyle)
+            return;
+
+        this._rebuildUpdate();
+    },
+
+    _attributeChanged: function(event)
+    {
+        // Any attribute removal or modification can affect the styles of "related" nodes.
+        // Do not touch the styles if they are being edited.
+        if (this._isEditingStyle || this._userOperation)
+            return;
+
+        if (!this._canAffectCurrentStyles(event.data.node))
+            return;
+
+        this._rebuildUpdate();
+    },
+
+    _canAffectCurrentStyles: function(node)
+    {
+        return this.node && (this.node === node || node.parentNode === this.node.parentNode || node.isAncestor(this.node));
+    },
+
+    _innerRefreshUpdate: function(node, computedStyle, editedSection)
+    {
+        for (var pseudoId in this.sections) {
+            var styleRules = this._refreshStyleRules(this.sections[pseudoId], computedStyle);
+            var usedProperties = {};
+            this._markUsedProperties(styleRules, usedProperties);
+            this._refreshSectionsForStyleRules(styleRules, usedProperties, editedSection);
+        }
+        if (computedStyle)
+            this.sections[0][0].rebuildComputedTrace(this.sections[0]);
+
+        this._nodeStylesUpdatedForTest(node, false);
+    },
+
+    _innerRebuildUpdate: function(node, styles)
+    {
+        this._sectionsContainer.removeChildren();
+        this._computedStylePane.bodyElement.removeChildren();
+        this._linkifier.reset();
+
+        var styleRules = this._rebuildStyleRules(node, styles);
+        var usedProperties = {};
+        this._markUsedProperties(styleRules, usedProperties);
+        this.sections[0] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, 0, null);
+        var anchorElement = this.sections[0].inheritedPropertiesSeparatorElement;
+
+        if (styles.computedStyle)
+            this.sections[0][0].rebuildComputedTrace(this.sections[0]);
+
+        for (var i = 0; i < styles.pseudoElements.length; ++i) {
+            var pseudoElementCSSRules = styles.pseudoElements[i];
+
+            styleRules = [];
+            var pseudoId = pseudoElementCSSRules.pseudoId;
+
+            var entry = { isStyleSeparator: true, pseudoId: pseudoId };
+            styleRules.push(entry);
+
+            // Add rules in reverse order to match the cascade order.
+            for (var j = pseudoElementCSSRules.rules.length - 1; j >= 0; --j) {
+                var rule = pseudoElementCSSRules.rules[j];
+                styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.sourceURL, rule: rule, editable: !!(rule.style && rule.style.id) });
+            }
+            usedProperties = {};
+            this._markUsedProperties(styleRules, usedProperties);
+            this.sections[pseudoId] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, pseudoId, anchorElement);
+        }
+
+        this._nodeStylesUpdatedForTest(node, true);
+    },
+
+    _nodeStylesUpdatedForTest: function(node, rebuild)
+    {
+        // Tests override this method.
+    },
+
+    _refreshStyleRules: function(sections, computedStyle)
+    {
+        var nodeComputedStyle = computedStyle;
+        var styleRules = [];
+        for (var i = 0; sections && i < sections.length; ++i) {
+            var section = sections[i];
+            if (section.isBlank)
+                continue;
+            if (section.computedStyle)
+                section.styleRule.style = nodeComputedStyle;
+            var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule, editable: !!(section.styleRule.style && section.styleRule.style.id), isAttribute: section.styleRule.isAttribute, isInherited: section.styleRule.isInherited };
+            styleRules.push(styleRule);
+        }
+        return styleRules;
+    },
+
+    _rebuildStyleRules: function(node, styles)
+    {
+        var nodeComputedStyle = styles.computedStyle;
+        this.sections = {};
+
+        var styleRules = [];
+
+        function addAttributesStyle()
+        {
+            if (!styles.attributesStyle)
+                return;
+            var attrStyle = { style: styles.attributesStyle, editable: false };
+            attrStyle.selectorText = node.nodeNameInCorrectCase() + "[" + WebInspector.UIString("Attributes Style") + "]";
+            styleRules.push(attrStyle);
+        }
+
+        styleRules.push({ computedStyle: true, selectorText: "", style: nodeComputedStyle, editable: false });
+
+        // Inline style has the greatest specificity.
+        if (styles.inlineStyle && node.nodeType() === Node.ELEMENT_NODE) {
+            var inlineStyle = { selectorText: "element.style", style: styles.inlineStyle, isAttribute: true };
+            styleRules.push(inlineStyle);
+        }
+
+        // Add rules in reverse order to match the cascade order.
+        if (styles.matchedCSSRules.length)
+            styleRules.push({ isStyleSeparator: true, text: WebInspector.UIString("Matched CSS Rules") });
+        var addedAttributesStyle;
+        for (var i = styles.matchedCSSRules.length - 1; i >= 0; --i) {
+            var rule = styles.matchedCSSRules[i];
+            if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent))
+                continue;
+            if ((rule.isUser || rule.isUserAgent) && !addedAttributesStyle) {
+                // Show element's Style Attributes after all author rules.
+                addedAttributesStyle = true;
+                addAttributesStyle();
+            }
+            styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.sourceURL, rule: rule, editable: !!(rule.style && rule.style.id) });
+        }
+
+        if (!addedAttributesStyle)
+            addAttributesStyle();
+
+        // Walk the node structure and identify styles with inherited properties.
+        var parentNode = node.parentNode;
+        function insertInheritedNodeSeparator(node)
+        {
+            var entry = {};
+            entry.isStyleSeparator = true;
+            entry.node = node;
+            styleRules.push(entry);
+        }
+
+        for (var parentOrdinal = 0; parentOrdinal < styles.inherited.length; ++parentOrdinal) {
+            var parentStyles = styles.inherited[parentOrdinal];
+            var separatorInserted = false;
+            if (parentStyles.inlineStyle) {
+                if (this._containsInherited(parentStyles.inlineStyle)) {
+                    var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: parentStyles.inlineStyle, isAttribute: true, isInherited: true, parentNode: parentNode };
+                    if (!separatorInserted) {
+                        insertInheritedNodeSeparator(parentNode);
+                        separatorInserted = true;
+                    }
+                    styleRules.push(inlineStyle);
+                }
+            }
+
+            for (var i = parentStyles.matchedCSSRules.length - 1; i >= 0; --i) {
+                var rulePayload = parentStyles.matchedCSSRules[i];
+                if (!this._containsInherited(rulePayload.style))
+                    continue;
+                var rule = rulePayload;
+                if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent))
+                    continue;
+
+                if (!separatorInserted) {
+                    insertInheritedNodeSeparator(parentNode);
+                    separatorInserted = true;
+                }
+                styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.sourceURL, rule: rule, isInherited: true, parentNode: parentNode, editable: !!(rule.style && rule.style.id) });
+            }
+            parentNode = parentNode.parentNode;
+        }
+        return styleRules;
+    },
+
+    _markUsedProperties: function(styleRules, usedProperties)
+    {
+        var foundImportantProperties = {};
+        var propertyToEffectiveRule = {};
+        for (var i = 0; i < styleRules.length; ++i) {
+            var styleRule = styleRules[i];
+            if (styleRule.computedStyle || styleRule.isStyleSeparator)
+                continue;
+            if (styleRule.section && styleRule.section.noAffect)
+                continue;
+
+            styleRule.usedProperties = {};
+
+            var style = styleRule.style;
+            var allProperties = style.allProperties;
+            for (var j = 0; j < allProperties.length; ++j) {
+                var property = allProperties[j];
+                if (!property.isLive || !property.parsedOk)
+                    continue;
+
+                var canonicalName = WebInspector.StylesSidebarPane.canonicalPropertyName(property.name);
+                // Do not pick non-inherited properties from inherited styles.
+                if (styleRule.isInherited && !WebInspector.CSSMetadata.InheritedProperties[canonicalName])
+                    continue;
+
+                if (foundImportantProperties.hasOwnProperty(canonicalName))
+                    continue;
+
+                var isImportant = property.priority.length;
+                if (!isImportant && usedProperties.hasOwnProperty(canonicalName))
+                    continue;
+
+                if (isImportant) {
+                    foundImportantProperties[canonicalName] = true;
+                    if (propertyToEffectiveRule.hasOwnProperty(canonicalName))
+                        delete propertyToEffectiveRule[canonicalName].usedProperties[canonicalName];
+                }
+
+                styleRule.usedProperties[canonicalName] = true;
+                usedProperties[canonicalName] = true;
+                propertyToEffectiveRule[canonicalName] = styleRule;
+            }
+        }
+    },
+
+    _refreshSectionsForStyleRules: function(styleRules, usedProperties, editedSection)
+    {
+        // Walk the style rules and update the sections with new overloaded and used properties.
+        for (var i = 0; i < styleRules.length; ++i) {
+            var styleRule = styleRules[i];
+            var section = styleRule.section;
+            if (styleRule.computedStyle) {
+                section._usedProperties = usedProperties;
+                section.update();
+            } else {
+                section._usedProperties = styleRule.usedProperties;
+                section.update(section === editedSection);
+            }
+        }
+    },
+
+    _rebuildSectionsForStyleRules: function(styleRules, usedProperties, pseudoId, anchorElement)
+    {
+        // Make a property section for each style rule.
+        var sections = [];
+        var lastWasSeparator = true;
+        for (var i = 0; i < styleRules.length; ++i) {
+            var styleRule = styleRules[i];
+            if (styleRule.isStyleSeparator) {
+                var separatorElement = document.createElement("div");
+                separatorElement.className = "sidebar-separator";
+                if (styleRule.node) {
+                    var link = WebInspector.DOMPresentationUtils.linkifyNodeReference(styleRule.node);
+                    separatorElement.appendChild(document.createTextNode(WebInspector.UIString("Inherited from") + " "));
+                    separatorElement.appendChild(link);
+                    if (!sections.inheritedPropertiesSeparatorElement)
+                        sections.inheritedPropertiesSeparatorElement = separatorElement;
+                } else if ("pseudoId" in styleRule) {
+                    var pseudoName = WebInspector.StylesSidebarPane.PseudoIdNames[styleRule.pseudoId];
+                    if (pseudoName)
+                        separatorElement.textContent = WebInspector.UIString("Pseudo ::%s element", pseudoName);
+                    else
+                        separatorElement.textContent = WebInspector.UIString("Pseudo element");
+                } else
+                    separatorElement.textContent = styleRule.text;
+                this._sectionsContainer.insertBefore(separatorElement, anchorElement);
+                lastWasSeparator = true;
+                continue;
+            }
+            var computedStyle = styleRule.computedStyle;
+
+            // Default editable to true if it was omitted.
+            var editable = styleRule.editable;
+            if (typeof editable === "undefined")
+                editable = true;
+
+            if (computedStyle)
+                var section = new WebInspector.ComputedStylePropertiesSection(this, styleRule, usedProperties);
+            else {
+                var section = new WebInspector.StylePropertiesSection(this, styleRule, editable, styleRule.isInherited, lastWasSeparator);
+                section._markSelectorMatches();
+            }
+            section.expanded = true;
+
+            if (computedStyle) {
+                this._computedStylePane.bodyElement.appendChild(section.element);
+                lastWasSeparator = true;
+            } else {
+                this._sectionsContainer.insertBefore(section.element, anchorElement);
+                lastWasSeparator = false;
+            }
+            sections.push(section);
+        }
+        return sections;
+    },
+
+    _containsInherited: function(style)
+    {
+        var properties = style.allProperties;
+        for (var i = 0; i < properties.length; ++i) {
+            var property = properties[i];
+            // Does this style contain non-overridden inherited property?
+            if (property.isLive && property.name in WebInspector.CSSMetadata.InheritedProperties)
+                return true;
+        }
+        return false;
+    },
+
+    _colorFormatSettingChanged: function(event)
+    {
+        this._updateColorFormatFilter();
+        for (var pseudoId in this.sections) {
+            var sections = this.sections[pseudoId];
+            for (var i = 0; i < sections.length; ++i)
+                sections[i].update(true);
+        }
+    },
+
+    _updateColorFormatFilter: function()
+    {
+        // Select the correct color format setting again, since it needs to be selected.
+        var selectedIndex = 0;
+        var value = WebInspector.settings.colorFormat.get();
+        var options = this.settingsSelectElement.options;
+        for (var i = 0; i < options.length; ++i) {
+            if (options[i].value === value) {
+                selectedIndex = i;
+                break;
+            }
+        }
+        this.settingsSelectElement.selectedIndex = selectedIndex;
+    },
+
+    _changeSetting: function(event)
+    {
+        var options = this.settingsSelectElement.options;
+        var selectedOption = options[this.settingsSelectElement.selectedIndex];
+        WebInspector.settings.colorFormat.set(selectedOption.value);
+    },
+
+    _createNewRule: function(event)
+    {
+        event.consume();
+        this.expand();
+        this.addBlankSection().startEditingSelector();
+    },
+
+    addBlankSection: function()
+    {
+        var blankSection = new WebInspector.BlankStylePropertiesSection(this, this.node ? this.node.appropriateSelectorFor(true) : "");
+
+        var elementStyleSection = this.sections[0][1];
+        this._sectionsContainer.insertBefore(blankSection.element, elementStyleSection.element.nextSibling);
+
+        this.sections[0].splice(2, 0, blankSection);
+
+        return blankSection;
+    },
+
+    removeSection: function(section)
+    {
+        for (var pseudoId in this.sections) {
+            var sections = this.sections[pseudoId];
+            var index = sections.indexOf(section);
+            if (index === -1)
+                continue;
+            sections.splice(index, 1);
+            if (section.element.parentNode)
+                section.element.parentNode.removeChild(section.element);
+        }
+    },
+
+    _toggleElementStatePane: function(event)
+    {
+        event.consume();
+        if (!this._elementStateButton.hasStyleClass("toggled")) {
+            this.expand();
+            this._elementStateButton.addStyleClass("toggled");
+            this._elementStatePane.addStyleClass("expanded");
+        } else {
+            this._elementStateButton.removeStyleClass("toggled");
+            this._elementStatePane.removeStyleClass("expanded");
+        }
+    },
+
+    _createElementStatePane: function()
+    {
+        this._elementStatePane = document.createElement("div");
+        this._elementStatePane.className = "styles-element-state-pane source-code";
+        var table = document.createElement("table");
+
+        var inputs = [];
+        this._elementStatePane.inputs = inputs;
+
+        function clickListener(event)
+        {
+            var node = this._validateNode();
+            if (!node)
+                return;
+            this._setPseudoClassCallback(node.id, event.target.state, event.target.checked);
+        }
+
+        function createCheckbox(state)
+        {
+            var td = document.createElement("td");
+            var label = document.createElement("label");
+            var input = document.createElement("input");
+            input.type = "checkbox";
+            input.state = state;
+            input.addEventListener("click", clickListener.bind(this), false);
+            inputs.push(input);
+            label.appendChild(input);
+            label.appendChild(document.createTextNode(":" + state));
+            td.appendChild(label);
+            return td;
+        }
+
+        var tr = document.createElement("tr");
+        tr.appendChild(createCheckbox.call(this, "active"));
+        tr.appendChild(createCheckbox.call(this, "hover"));
+        table.appendChild(tr);
+
+        tr = document.createElement("tr");
+        tr.appendChild(createCheckbox.call(this, "focus"));
+        tr.appendChild(createCheckbox.call(this, "visited"));
+        table.appendChild(tr);
+
+        this._elementStatePane.appendChild(table);
+    },
+
+    _showUserAgentStylesSettingChanged: function()
+    {
+        this._rebuildUpdate();
+    },
+
+    willHide: function()
+    {
+        this._spectrumHelper.hide();
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.ComputedStyleSidebarPane = function()
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Computed Style"));
+    var showInheritedCheckbox = new WebInspector.Checkbox(WebInspector.UIString("Show inherited"), "sidebar-pane-subtitle");
+    this.titleElement.appendChild(showInheritedCheckbox.element);
+
+    if (WebInspector.settings.showInheritedComputedStyleProperties.get()) {
+        this.bodyElement.addStyleClass("show-inherited");
+        showInheritedCheckbox.checked = true;
+    }
+
+    function showInheritedToggleFunction(event)
+    {
+        WebInspector.settings.showInheritedComputedStyleProperties.set(showInheritedCheckbox.checked);
+        if (WebInspector.settings.showInheritedComputedStyleProperties.get())
+            this.bodyElement.addStyleClass("show-inherited");
+        else
+            this.bodyElement.removeStyleClass("show-inherited");
+    }
+
+    showInheritedCheckbox.addEventListener(showInheritedToggleFunction.bind(this));
+}
+
+WebInspector.ComputedStyleSidebarPane.prototype = {
+    wasShown: function()
+    {
+        WebInspector.SidebarPane.prototype.wasShown.call(this);
+        if (!this._hasFreshContent)
+            this.prepareContent();
+    },
+
+    /**
+     * @param {function()=} callback
+     */
+    prepareContent: function(callback)
+    {
+        function wrappedCallback() {
+            this._hasFreshContent = true;
+            if (callback)
+                callback();
+            delete this._hasFreshContent;
+        }
+        this._stylesSidebarPane._refreshUpdate(null, true, wrappedCallback.bind(this));
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.PropertiesSection}
+ */
+WebInspector.StylePropertiesSection = function(parentPane, styleRule, editable, isInherited, isFirstSection)
+{
+    WebInspector.PropertiesSection.call(this, "");
+    this.element.className = "styles-section matched-styles monospace" + (isFirstSection ? " first-styles-section" : "");
+    // We don't really use properties' disclosure.
+    this.propertiesElement.removeStyleClass("properties-tree");
+
+    if (styleRule.media) {
+        for (var i = styleRule.media.length - 1; i >= 0; --i) {
+            var media = styleRule.media[i];
+            var mediaDataElement = this.titleElement.createChild("div", "media");
+            var mediaText;
+            switch (media.source) {
+            case WebInspector.CSSMedia.Source.LINKED_SHEET:
+            case WebInspector.CSSMedia.Source.INLINE_SHEET:
+                mediaText = "media=\"" + media.text + "\"";
+                break;
+            case WebInspector.CSSMedia.Source.MEDIA_RULE:
+                mediaText = "@media " + media.text;
+                break;
+            case WebInspector.CSSMedia.Source.IMPORT_RULE:
+                mediaText = "@import " + media.text;
+                break;
+            }
+
+            if (media.sourceURL) {
+                var refElement = mediaDataElement.createChild("div", "subtitle");
+                var lineNumber = media.sourceLine < 0 ? undefined : media.sourceLine;
+                var anchor = WebInspector.linkifyResourceAsNode(media.sourceURL, lineNumber, "subtitle", media.sourceURL + (isNaN(lineNumber) ? "" : (":" + (lineNumber + 1))));
+                anchor.preferredPanel = "scripts";
+                anchor.style.float = "right";
+                refElement.appendChild(anchor);
+            }
+
+            var mediaTextElement = mediaDataElement.createChild("span");
+            mediaTextElement.textContent = mediaText;
+            mediaTextElement.title = media.text;
+        }
+    }
+
+    var selectorContainer = document.createElement("div");
+    this._selectorElement = document.createElement("span");
+    this._selectorElement.textContent = styleRule.selectorText;
+    selectorContainer.appendChild(this._selectorElement);
+
+    var openBrace = document.createElement("span");
+    openBrace.textContent = " {";
+    selectorContainer.appendChild(openBrace);
+    selectorContainer.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false);
+    selectorContainer.addEventListener("click", this._handleSelectorContainerClick.bind(this), false);
+
+    var closeBrace = document.createElement("div");
+    closeBrace.textContent = "}";
+    this.element.appendChild(closeBrace);
+
+    this._selectorElement.addEventListener("click", this._handleSelectorClick.bind(this), false);
+    this.element.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false);
+    this.element.addEventListener("click", this._handleEmptySpaceClick.bind(this), false);
+
+    this._parentPane = parentPane;
+    this.styleRule = styleRule;
+    this.rule = this.styleRule.rule;
+    this.editable = editable;
+    this.isInherited = isInherited;
+
+    if (this.rule) {
+        // Prevent editing the user agent and user rules.
+        if (this.rule.isUserAgent || this.rule.isUser)
+            this.editable = false;
+        else {
+            // Check this is a real CSSRule, not a bogus object coming from WebInspector.BlankStylePropertiesSection.
+            if (this.rule.id)
+                this.navigable = this.rule.isSourceNavigable();
+        }
+        this.titleElement.addStyleClass("styles-selector");
+    }
+
+    this._usedProperties = styleRule.usedProperties;
+
+    this._selectorRefElement = document.createElement("div");
+    this._selectorRefElement.className = "subtitle";
+    this._selectorRefElement.appendChild(this._createRuleOriginNode());
+    selectorContainer.insertBefore(this._selectorRefElement, selectorContainer.firstChild);
+    this.titleElement.appendChild(selectorContainer);
+    this._selectorContainer = selectorContainer;
+
+    if (isInherited)
+        this.element.addStyleClass("show-inherited"); // This one is related to inherited rules, not computed style.
+
+    if (this.navigable)
+        this.element.addStyleClass("navigable");
+
+    if (!this.editable)
+        this.element.addStyleClass("read-only");
+}
+
+WebInspector.StylePropertiesSection.prototype = {
+    get pane()
+    {
+        return this._parentPane;
+    },
+
+    collapse: function(dontRememberState)
+    {
+        // Overriding with empty body.
+    },
+
+    isPropertyInherited: function(propertyName)
+    {
+        if (this.isInherited) {
+            // While rendering inherited stylesheet, reverse meaning of this property.
+            // Render truly inherited properties with black, i.e. return them as non-inherited.
+            return !(propertyName in WebInspector.CSSMetadata.InheritedProperties);
+        }
+        return false;
+    },
+
+    /**
+     * @param {string} propertyName
+     * @param {boolean=} isShorthand
+     */
+    isPropertyOverloaded: function(propertyName, isShorthand)
+    {
+        if (!this._usedProperties || this.noAffect)
+            return false;
+
+        if (this.isInherited && !(propertyName in WebInspector.CSSMetadata.InheritedProperties)) {
+            // In the inherited sections, only show overrides for the potentially inherited properties.
+            return false;
+        }
+
+        var canonicalName = WebInspector.StylesSidebarPane.canonicalPropertyName(propertyName);
+        var used = (canonicalName in this._usedProperties);
+        if (used || !isShorthand)
+            return !used;
+
+        // Find out if any of the individual longhand properties of the shorthand
+        // are used, if none are then the shorthand is overloaded too.
+        var longhandProperties = this.styleRule.style.longhandProperties(propertyName);
+        for (var j = 0; j < longhandProperties.length; ++j) {
+            var individualProperty = longhandProperties[j];
+            if (WebInspector.StylesSidebarPane.canonicalPropertyName(individualProperty.name) in this._usedProperties)
+                return false;
+        }
+
+        return true;
+    },
+
+    nextEditableSibling: function()
+    {
+        var curSection = this;
+        do {
+            curSection = curSection.nextSibling;
+        } while (curSection && !curSection.editable);
+
+        if (!curSection) {
+            curSection = this.firstSibling;
+            while (curSection && !curSection.editable)
+                curSection = curSection.nextSibling;
+        }
+
+        return (curSection && curSection.editable) ? curSection : null;
+    },
+
+    previousEditableSibling: function()
+    {
+        var curSection = this;
+        do {
+            curSection = curSection.previousSibling;
+        } while (curSection && !curSection.editable);
+
+        if (!curSection) {
+            curSection = this.lastSibling;
+            while (curSection && !curSection.editable)
+                curSection = curSection.previousSibling;
+        }
+
+        return (curSection && curSection.editable) ? curSection : null;
+    },
+
+    update: function(full)
+    {
+        if (this.styleRule.selectorText)
+            this._selectorElement.textContent = this.styleRule.selectorText;
+        this._markSelectorMatches();
+        if (full) {
+            this.propertiesTreeOutline.removeChildren();
+            this.populated = false;
+        } else {
+            var child = this.propertiesTreeOutline.children[0];
+            while (child) {
+                child.overloaded = this.isPropertyOverloaded(child.name, child.isShorthand);
+                child = child.traverseNextTreeElement(false, null, true);
+            }
+        }
+        this.afterUpdate();
+    },
+
+    afterUpdate: function()
+    {
+        if (this._afterUpdate) {
+            this._afterUpdate(this);
+            delete this._afterUpdate;
+        }
+    },
+
+    onpopulate: function()
+    {
+        var style = this.styleRule.style;
+        var allProperties = style.allProperties;
+        this.uniqueProperties = [];
+
+        var styleHasEditableSource = this.editable && !!style.range;
+        if (styleHasEditableSource) {
+            for (var i = 0; i < allProperties.length; ++i) {
+                var property = allProperties[i];
+                this.uniqueProperties.push(property);
+                if (property.styleBased)
+                    continue;
+
+                var isShorthand = !!WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(property.name);
+                var inherited = this.isPropertyInherited(property.name);
+                var overloaded = property.inactive || this.isPropertyOverloaded(property.name);
+                var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded);
+                this.propertiesTreeOutline.appendChild(item);
+            }
+            return;
+        }
+
+        var generatedShorthands = {};
+        // For style-based properties, generate shorthands with values when possible.
+        for (var i = 0; i < allProperties.length; ++i) {
+            var property = allProperties[i];
+            this.uniqueProperties.push(property);
+            var isShorthand = !!WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(property.name);
+
+            // For style-based properties, try generating shorthands.
+            var shorthands = isShorthand ? null : WebInspector.CSSMetadata.cssPropertiesMetainfo.shorthands(property.name);
+            var shorthandPropertyAvailable = false;
+            for (var j = 0; shorthands && !shorthandPropertyAvailable && j < shorthands.length; ++j) {
+                var shorthand = shorthands[j];
+                if (shorthand in generatedShorthands) {
+                    shorthandPropertyAvailable = true;
+                    continue;  // There already is a shorthand this longhands falls under.
+                }
+                if (style.getLiveProperty(shorthand)) {
+                    shorthandPropertyAvailable = true;
+                    continue;  // There is an explict shorthand property this longhands falls under.
+                }
+                if (!style.shorthandValue(shorthand)) {
+                    shorthandPropertyAvailable = false;
+                    continue;  // Never generate synthetic shorthands when no value is available.
+                }
+
+                // Generate synthetic shorthand we have a value for.
+                var shorthandProperty = new WebInspector.CSSProperty(style, style.allProperties.length, shorthand, style.shorthandValue(shorthand), "", "style", true, true);
+                var overloaded = property.inactive || this.isPropertyOverloaded(property.name, true);
+                var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, shorthandProperty,  /* isShorthand */ true, /* inherited */ false, overloaded);
+                this.propertiesTreeOutline.appendChild(item);
+                generatedShorthands[shorthand] = shorthandProperty;
+                shorthandPropertyAvailable = true;
+            }
+            if (shorthandPropertyAvailable)
+                continue;  // Shorthand for the property found.
+
+            var inherited = this.isPropertyInherited(property.name);
+            var overloaded = property.inactive || this.isPropertyOverloaded(property.name, isShorthand);
+            var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded);
+            this.propertiesTreeOutline.appendChild(item);
+        }
+    },
+
+    findTreeElementWithName: function(name)
+    {
+        var treeElement = this.propertiesTreeOutline.children[0];
+        while (treeElement) {
+            if (treeElement.name === name)
+                return treeElement;
+            treeElement = treeElement.traverseNextTreeElement(true, null, true);
+        }
+        return null;
+    },
+
+    _markSelectorMatches: function()
+    {
+        var rule = this.styleRule.rule;
+        if (!rule)
+            return;
+
+        var matchingSelectors = rule.matchingSelectors;
+        // .selector is rendered as non-affecting selector by default.
+        if (this.noAffect || matchingSelectors)
+            this._selectorElement.className = "selector";
+        if (!matchingSelectors)
+            return;
+
+        var selectors = rule.selectors;
+        var fragment = document.createDocumentFragment();
+        var currentMatch = 0;
+        for (var i = 0, lastSelectorIndex = selectors.length - 1; i <= lastSelectorIndex ; ++i) {
+            var selectorNode;
+            var textNode = document.createTextNode(selectors[i]);
+            if (matchingSelectors[currentMatch] === i) {
+                ++currentMatch;
+                selectorNode = document.createElement("span");
+                selectorNode.className = "selector-matches";
+                selectorNode.appendChild(textNode);
+            } else
+                selectorNode = textNode;
+
+            fragment.appendChild(selectorNode);
+            if (i !== lastSelectorIndex)
+                fragment.appendChild(document.createTextNode(", "));
+        }
+
+        this._selectorElement.removeChildren();
+        this._selectorElement.appendChild(fragment);
+    },
+
+    _checkWillCancelEditing: function()
+    {
+        var willCauseCancelEditing = this._willCauseCancelEditing;
+        delete this._willCauseCancelEditing;
+        return willCauseCancelEditing;
+    },
+
+    _handleSelectorContainerClick: function(event)
+    {
+        if (this._checkWillCancelEditing() || !this.editable)
+            return;
+        if (event.target === this._selectorContainer)
+            this.addNewBlankProperty(0).startEditing();
+    },
+
+    /**
+     * @param {number=} index
+     */
+    addNewBlankProperty: function(index)
+    {
+        var style = this.styleRule.style;
+        var property = style.newBlankProperty(index);
+        var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, false, false, false);
+        index = property.index;
+        this.propertiesTreeOutline.insertChild(item, index);
+        item.listItemElement.textContent = "";
+        item._newProperty = true;
+        item.updateTitle();
+        return item;
+    },
+
+    _createRuleOriginNode: function()
+    {
+        /**
+         * @param {string} url
+         * @param {number} line
+         */
+        function linkifyUncopyable(url, line)
+        {
+            var link = WebInspector.linkifyResourceAsNode(url, line, "", url + ":" + (line + 1));
+            link.preferredPanel = "scripts";
+            link.classList.add("webkit-html-resource-link");
+            link.setAttribute("data-uncopyable", link.textContent);
+            link.textContent = "";
+            return link;
+        }
+
+        if (this.styleRule.sourceURL)
+            return this._parentPane._linkifier.linkifyCSSRuleLocation(this.rule) || linkifyUncopyable(this.styleRule.sourceURL, this.rule.sourceLine);
+
+        if (!this.rule)
+            return document.createTextNode("");
+
+        var origin = "";
+        if (this.rule.isUserAgent)
+            return document.createTextNode(WebInspector.UIString("user agent stylesheet"));
+        if (this.rule.isUser)
+            return document.createTextNode(WebInspector.UIString("user stylesheet"));
+        if (this.rule.isViaInspector) {
+            var element = document.createElement("span");
+            var resource = WebInspector.cssModel.viaInspectorResourceForRule(this.rule);
+            if (resource)
+                element.appendChild(linkifyUncopyable(resource.url, this.rule.sourceLine));
+            else
+                element.textContent = WebInspector.UIString("via inspector");
+            return element;
+        }
+    },
+
+    _handleEmptySpaceMouseDown: function(event)
+    {
+        this._willCauseCancelEditing = this._parentPane._isEditingStyle;
+    },
+
+    _handleEmptySpaceClick: function(event)
+    {
+        if (!this.editable)
+            return;
+
+        if (!window.getSelection().isCollapsed)
+            return;
+
+        if (this._checkWillCancelEditing())
+            return;
+
+        if (event.target.hasStyleClass("header") || this.element.hasStyleClass("read-only") || event.target.enclosingNodeOrSelfWithClass("media")) {
+            event.consume();
+            return;
+        }
+        this.expand();
+        this.addNewBlankProperty().startEditing();
+    },
+
+    _handleSelectorClick: function(event)
+    {
+        this._startEditingOnMouseEvent();
+        event.consume(true);
+    },
+
+    _startEditingOnMouseEvent: function()
+    {
+        if (!this.editable)
+            return;
+
+        if (!this.rule && this.propertiesTreeOutline.children.length === 0) {
+            this.expand();
+            this.addNewBlankProperty().startEditing();
+            return;
+        }
+
+        if (!this.rule)
+            return;
+
+        this.startEditingSelector();
+    },
+
+    startEditingSelector: function()
+    {
+        var element = this._selectorElement;
+        if (WebInspector.isBeingEdited(element))
+            return;
+
+        element.scrollIntoViewIfNeeded(false);
+        element.textContent = element.textContent; // Reset selector marks in group.
+
+        var config = new WebInspector.EditingConfig(this.editingSelectorCommitted.bind(this), this.editingSelectorCancelled.bind(this));
+        WebInspector.startEditing(this._selectorElement, config);
+
+        window.getSelection().setBaseAndExtent(element, 0, element, 1);
+    },
+
+    _moveEditorFromSelector: function(moveDirection)
+    {
+        this._markSelectorMatches();
+
+        if (!moveDirection)
+            return;
+
+        if (moveDirection === "forward") {
+            this.expand();
+            var firstChild = this.propertiesTreeOutline.children[0];
+            while (firstChild && firstChild.inherited)
+                firstChild = firstChild.nextSibling;
+            if (!firstChild)
+                this.addNewBlankProperty().startEditing();
+            else
+                firstChild.startEditing(firstChild.nameElement);
+        } else {
+            var previousSection = this.previousEditableSibling();
+            if (!previousSection)
+                return;
+
+            previousSection.expand();
+            previousSection.addNewBlankProperty().startEditing();
+        }
+    },
+
+    editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection)
+    {
+        if (newContent)
+            newContent = newContent.trim();
+        if (newContent === oldContent) {
+            // Revert to a trimmed version of the selector if need be.
+            this._selectorElement.textContent = newContent;
+            return this._moveEditorFromSelector(moveDirection);
+        }
+
+        var selectedNode = this._parentPane.node;
+
+        function successCallback(newRule, doesAffectSelectedNode)
+        {
+            if (!doesAffectSelectedNode) {
+                this.noAffect = true;
+                this.element.addStyleClass("no-affect");
+            } else {
+                delete this.noAffect;
+                this.element.removeStyleClass("no-affect");
+            }
+
+            this.rule = newRule;
+            this.styleRule = { section: this, style: newRule.style, selectorText: newRule.selectorText, media: newRule.media, sourceURL: newRule.sourceURL, rule: newRule };
+
+            this._parentPane.update(selectedNode);
+
+            finishOperationAndMoveEditor.call(this, moveDirection);
+        }
+
+        function finishOperationAndMoveEditor(direction)
+        {
+            delete this._parentPane._userOperation;
+            this._moveEditorFromSelector(direction);
+        }
+
+        // This gets deleted in finishOperationAndMoveEditor(), which is called both on success and failure.
+        this._parentPane._userOperation = true;
+        WebInspector.cssModel.setRuleSelector(this.rule.id, selectedNode ? selectedNode.id : 0, newContent, successCallback.bind(this), finishOperationAndMoveEditor.bind(this, moveDirection));
+    },
+
+    editingSelectorCancelled: function()
+    {
+        // Do nothing but mark the selectors in group if necessary.
+        // This is overridden by BlankStylePropertiesSection.
+        this._markSelectorMatches();
+    },
+
+    __proto__: WebInspector.PropertiesSection.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.PropertiesSection}
+ * @param {!WebInspector.StylesSidebarPane} stylesPane
+ * @param {!Object} styleRule
+ * @param {!Object.<string, boolean>} usedProperties
+ */
+WebInspector.ComputedStylePropertiesSection = function(stylesPane, styleRule, usedProperties)
+{
+    WebInspector.PropertiesSection.call(this, "");
+    this.headerElement.addStyleClass("hidden");
+    this.element.className = "styles-section monospace first-styles-section read-only computed-style";
+    this._stylesPane = stylesPane;
+    this.styleRule = styleRule;
+    this._usedProperties = usedProperties;
+    this._alwaysShowComputedProperties = { "display": true, "height": true, "width": true };
+    this.computedStyle = true;
+    this._propertyTreeElements = {};
+    this._expandedPropertyNames = {};
+}
+
+WebInspector.ComputedStylePropertiesSection.prototype = {
+    collapse: function(dontRememberState)
+    {
+        // Overriding with empty body.
+    },
+
+    _isPropertyInherited: function(propertyName)
+    {
+        var canonicalName = WebInspector.StylesSidebarPane.canonicalPropertyName(propertyName);
+        return !(canonicalName in this._usedProperties) && !(canonicalName in this._alwaysShowComputedProperties);
+    },
+
+    update: function()
+    {
+        this._expandedPropertyNames = {};
+        for (var name in this._propertyTreeElements) {
+            if (this._propertyTreeElements[name].expanded)
+                this._expandedPropertyNames[name] = true;
+        }
+        this._propertyTreeElements = {};
+        this.propertiesTreeOutline.removeChildren();
+        this.populated = false;
+    },
+
+    onpopulate: function()
+    {
+        function sorter(a, b)
+        {
+            return a.name.compareTo(b.name);
+        }
+
+        var style = this.styleRule.style;
+        if (!style)
+            return;
+
+        var uniqueProperties = [];
+        var allProperties = style.allProperties;
+        for (var i = 0; i < allProperties.length; ++i)
+            uniqueProperties.push(allProperties[i]);
+        uniqueProperties.sort(sorter);
+
+        this._propertyTreeElements = {};
+        for (var i = 0; i < uniqueProperties.length; ++i) {
+            var property = uniqueProperties[i];
+            var inherited = this._isPropertyInherited(property.name);
+            var item = new WebInspector.ComputedStylePropertyTreeElement(this._stylesPane, this.styleRule, style, property, inherited);
+            this.propertiesTreeOutline.appendChild(item);
+            this._propertyTreeElements[property.name] = item;
+        }
+    },
+
+    rebuildComputedTrace: function(sections)
+    {
+        for (var i = 0; i < sections.length; ++i) {
+            var section = sections[i];
+            if (section.computedStyle || section.isBlank)
+                continue;
+
+            for (var j = 0; j < section.uniqueProperties.length; ++j) {
+                var property = section.uniqueProperties[j];
+                if (property.disabled)
+                    continue;
+                if (section.isInherited && !(property.name in WebInspector.CSSMetadata.InheritedProperties))
+                    continue;
+
+                var treeElement = this._propertyTreeElements[property.name];
+                if (treeElement) {
+                    var fragment = document.createDocumentFragment();
+                    var selector = fragment.createChild("span");
+                    selector.style.color = "gray";
+                    selector.textContent = section.styleRule.selectorText;
+                    fragment.appendChild(document.createTextNode(" - " + property.value + " "));
+                    var subtitle = fragment.createChild("span");
+                    subtitle.style.float = "right";
+                    subtitle.appendChild(section._createRuleOriginNode());
+                    var childElement = new TreeElement(fragment, null, false);
+                    treeElement.appendChild(childElement);
+                    if (property.inactive || section.isPropertyOverloaded(property.name))
+                        childElement.listItemElement.addStyleClass("overloaded");
+                    if (!property.parsedOk) {
+                        childElement.listItemElement.addStyleClass("not-parsed-ok");
+                        childElement.listItemElement.insertBefore(WebInspector.StylesSidebarPane.createExclamationMark(property.name), childElement.listItemElement.firstChild);
+                    }
+                }
+            }
+        }
+
+        // Restore expanded state after update.
+        for (var name in this._expandedPropertyNames) {
+            if (name in this._propertyTreeElements)
+                this._propertyTreeElements[name].expand();
+        }
+    },
+
+    __proto__: WebInspector.PropertiesSection.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StylePropertiesSection}
+ * @param {WebInspector.StylesSidebarPane} stylesPane
+ * @param {string} defaultSelectorText
+ */
+WebInspector.BlankStylePropertiesSection = function(stylesPane, defaultSelectorText)
+{
+    WebInspector.StylePropertiesSection.call(this, stylesPane, {selectorText: defaultSelectorText, rule: {isViaInspector: true}}, true, false, false);
+    this.element.addStyleClass("blank-section");
+}
+
+WebInspector.BlankStylePropertiesSection.prototype = {
+    get isBlank()
+    {
+        return !this._normal;
+    },
+
+    expand: function()
+    {
+        if (!this.isBlank)
+            WebInspector.StylePropertiesSection.prototype.expand.call(this);
+    },
+
+    editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection)
+    {
+        if (!this.isBlank) {
+            WebInspector.StylePropertiesSection.prototype.editingSelectorCommitted.call(this, element, newContent, oldContent, context, moveDirection);
+            return;
+        }
+
+        function successCallback(newRule, doesSelectorAffectSelectedNode)
+        {
+            var styleRule = { section: this, style: newRule.style, selectorText: newRule.selectorText, sourceURL: newRule.sourceURL, rule: newRule };
+            this.makeNormal(styleRule);
+
+            if (!doesSelectorAffectSelectedNode) {
+                this.noAffect = true;
+                this.element.addStyleClass("no-affect");
+            }
+
+            this._selectorRefElement.removeChildren();
+            this._selectorRefElement.appendChild(this._createRuleOriginNode());
+            this.expand();
+            if (this.element.parentElement) // Might have been detached already.
+                this._moveEditorFromSelector(moveDirection);
+
+            this._markSelectorMatches();
+            delete this._parentPane._userOperation;
+        }
+
+        if (newContent)
+            newContent = newContent.trim();
+        this._parentPane._userOperation = true;
+        WebInspector.cssModel.addRule(this.pane.node.id, newContent, successCallback.bind(this), this.editingSelectorCancelled.bind(this));
+    },
+
+    editingSelectorCancelled: function()
+    {
+        delete this._parentPane._userOperation;
+        if (!this.isBlank) {
+            WebInspector.StylePropertiesSection.prototype.editingSelectorCancelled.call(this);
+            return;
+        }
+
+        this.pane.removeSection(this);
+    },
+
+    makeNormal: function(styleRule)
+    {
+        this.element.removeStyleClass("blank-section");
+        this.styleRule = styleRule;
+        this.rule = styleRule.rule;
+
+        // FIXME: replace this instance by a normal WebInspector.StylePropertiesSection.
+        this._normal = true;
+    },
+
+    __proto__: WebInspector.StylePropertiesSection.prototype
+}
+
+/**
+ * @constructor
+ * @extends {TreeElement}
+ * @param {Object} styleRule
+ * @param {WebInspector.CSSStyleDeclaration} style
+ * @param {WebInspector.CSSProperty} property
+ * @param {boolean} inherited
+ * @param {boolean} overloaded
+ * @param {boolean} hasChildren
+ */
+WebInspector.StylePropertyTreeElementBase = function(styleRule, style, property, inherited, overloaded, hasChildren)
+{
+    this._styleRule = styleRule;
+    this.style = style;
+    this.property = property;
+    this._inherited = inherited;
+    this._overloaded = overloaded;
+
+    // Pass an empty title, the title gets made later in onattach.
+    TreeElement.call(this, "", null, hasChildren);
+
+    this.selectable = false;
+}
+
+WebInspector.StylePropertyTreeElementBase.prototype = {
+    /**
+     * @return {?WebInspector.DOMNode}
+     */
+    node: function()
+    {
+        return null;  // Overridden by ancestors.
+    },
+
+    /**
+     * @return {?WebInspector.StylesSidebarPane}
+     */
+    editablePane: function()
+    {
+        return null;  // Overridden by ancestors.
+    },
+
+    get inherited()
+    {
+        return this._inherited;
+    },
+
+    set inherited(x)
+    {
+        if (x === this._inherited)
+            return;
+        this._inherited = x;
+        this.updateState();
+    },
+
+    get overloaded()
+    {
+        return this._overloaded;
+    },
+
+    set overloaded(x)
+    {
+        if (x === this._overloaded)
+            return;
+        this._overloaded = x;
+        this.updateState();
+    },
+
+    get disabled()
+    {
+        return this.property.disabled;
+    },
+
+    get name()
+    {
+        if (!this.disabled || !this.property.text)
+            return this.property.name;
+
+        var text = this.property.text;
+        var index = text.indexOf(":");
+        if (index < 1)
+            return this.property.name;
+
+        text = text.substring(0, index).trim();
+        if (text.startsWith("/*"))
+            text = text.substring(2).trim();
+        return text;
+    },
+
+    get priority()
+    {
+        if (this.disabled)
+            return ""; // rely upon raw text to render it in the value field
+        return this.property.priority;
+    },
+
+    get value()
+    {
+        if (!this.disabled || !this.property.text)
+            return this.property.value;
+
+        var match = this.property.text.match(/(.*);\s*/);
+        if (!match || !match[1])
+            return this.property.value;
+
+        var text = match[1];
+        var index = text.indexOf(":");
+        if (index < 1)
+            return this.property.value;
+
+        return text.substring(index + 1).trim();
+    },
+
+    get parsedOk()
+    {
+        return this.property.parsedOk;
+    },
+
+    onattach: function()
+    {
+        this.updateTitle();
+    },
+
+    updateTitle: function()
+    {
+        var value = this.value;
+
+        this.updateState();
+
+        var nameElement = document.createElement("span");
+        nameElement.className = "webkit-css-property";
+        nameElement.textContent = this.name;
+        nameElement.title = this.property.propertyText;
+        this.nameElement = nameElement;
+
+        this._expandElement = document.createElement("span");
+        this._expandElement.className = "expand-element";
+
+        var valueElement = document.createElement("span");
+        valueElement.className = "value";
+        this.valueElement = valueElement;
+
+        var cf = WebInspector.Color.Format;
+
+        if (value) {
+            var self = this;
+
+            function processValue(regex, processor, nextProcessor, valueText)
+            {
+                var container = document.createDocumentFragment();
+
+                var items = valueText.replace(regex, "\0$1\0").split("\0");
+                for (var i = 0; i < items.length; ++i) {
+                    if ((i % 2) === 0) {
+                        if (nextProcessor)
+                            container.appendChild(nextProcessor(items[i]));
+                        else
+                            container.appendChild(document.createTextNode(items[i]));
+                    } else {
+                        var processedNode = processor(items[i]);
+                        if (processedNode)
+                            container.appendChild(processedNode);
+                    }
+                }
+
+                return container;
+            }
+
+            function linkifyURL(url)
+            {
+                var hrefUrl = url;
+                var match = hrefUrl.match(/['"]?([^'"]+)/);
+                if (match)
+                    hrefUrl = match[1];
+                var container = document.createDocumentFragment();
+                container.appendChild(document.createTextNode("url("));
+                if (self._styleRule.sourceURL)
+                    hrefUrl = WebInspector.ParsedURL.completeURL(self._styleRule.sourceURL, hrefUrl);
+                else if (self.node())
+                    hrefUrl = self.node().resolveURL(hrefUrl);
+                var hasResource = !!WebInspector.resourceForURL(hrefUrl);
+                // FIXME: WebInspector.linkifyURLAsNode() should really use baseURI.
+                container.appendChild(WebInspector.linkifyURLAsNode(hrefUrl, url, undefined, !hasResource));
+                container.appendChild(document.createTextNode(")"));
+                return container;
+            }
+
+            function processColor(text)
+            {
+                var color = WebInspector.Color.parse(text);
+
+                // We can be called with valid non-color values of |text| (like 'none' from border style)
+                if (!color)
+                    return document.createTextNode(text);
+
+                var format = getFormat();
+                var spectrumHelper = self.editablePane() && self.editablePane()._spectrumHelper;
+                var spectrum = spectrumHelper ? spectrumHelper.spectrum() : null;
+
+                var colorSwatch = new WebInspector.ColorSwatch();
+                colorSwatch.setColorString(text);
+                colorSwatch.element.addEventListener("click", swatchClick, false);
+
+                var scrollerElement;
+
+                function spectrumChanged(e)
+                {
+                    var colorString = /** @type {string} */ (e.data);
+                    spectrum.displayText = colorString;
+                    colorValueElement.textContent = colorString;
+                    colorSwatch.setColorString(colorString);
+                    self.applyStyleText(nameElement.textContent + ": " + valueElement.textContent, false, false, false);
+                }
+
+                function spectrumHidden(event)
+                {
+                    if (scrollerElement)
+                        scrollerElement.removeEventListener("scroll", repositionSpectrum, false);
+                    var commitEdit = event.data;
+                    var propertyText = !commitEdit && self.originalPropertyText ? self.originalPropertyText : (nameElement.textContent + ": " + valueElement.textContent);
+                    self.applyStyleText(propertyText, true, true, false);
+                    spectrum.removeEventListener(WebInspector.Spectrum.Events.ColorChanged, spectrumChanged);
+                    spectrumHelper.removeEventListener(WebInspector.SpectrumPopupHelper.Events.Hidden, spectrumHidden);
+
+                    delete self.editablePane()._isEditingStyle;
+                    delete self.originalPropertyText;
+                }
+
+                function repositionSpectrum()
+                {
+                    spectrumHelper.reposition(colorSwatch.element);
+                }
+
+                function swatchClick(e)
+                {
+                    // Shift + click toggles color formats.
+                    // Click opens colorpicker, only if the element is not in computed styles section.
+                    if (!spectrumHelper || e.shiftKey)
+                        changeColorDisplay(e);
+                    else {
+                        var visible = spectrumHelper.toggle(colorSwatch.element, color, format);
+
+                        if (visible) {
+                            spectrum.displayText = color.toString(format);
+                            self.originalPropertyText = self.property.propertyText;
+                            self.editablePane()._isEditingStyle = true;
+                            spectrum.addEventListener(WebInspector.Spectrum.Events.ColorChanged, spectrumChanged);
+                            spectrumHelper.addEventListener(WebInspector.SpectrumPopupHelper.Events.Hidden, spectrumHidden);
+
+                            scrollerElement = colorSwatch.element.enclosingNodeOrSelfWithClass("scroll-target");
+                            if (scrollerElement)
+                                scrollerElement.addEventListener("scroll", repositionSpectrum, false);
+                            else
+                                console.error("Unable to handle color picker scrolling");
+                        }
+                    }
+                    e.consume(true);
+                }
+
+                function getFormat()
+                {
+                    var format;
+                    var formatSetting = WebInspector.settings.colorFormat.get();
+                    if (formatSetting === cf.Original)
+                        format = cf.Original;
+                    else if (formatSetting === cf.RGB)
+                        format = (color.hasAlpha() ? cf.RGBA : cf.RGB);
+                    else if (formatSetting === cf.HSL)
+                        format = (color.hasAlpha() ? cf.HSLA : cf.HSL);
+                    else if (!color.hasAlpha())
+                        format = (color.canBeShortHex() ? cf.ShortHEX : cf.HEX);
+                    else
+                        format = cf.RGBA;
+
+                    return format;
+                }
+
+                var colorValueElement = document.createElement("span");
+                colorValueElement.textContent = color.toString(format);
+
+                function nextFormat(curFormat)
+                {
+                    // The format loop is as follows:
+                    // * original
+                    // * rgb(a)
+                    // * hsl(a)
+                    // * nickname (if the color has a nickname)
+                    // * if the color is simple:
+                    //   - shorthex (if has short hex)
+                    //   - hex
+                    switch (curFormat) {
+                        case cf.Original:
+                            return !color.hasAlpha() ? cf.RGB : cf.RGBA;
+
+                        case cf.RGB:
+                        case cf.RGBA:
+                            return !color.hasAlpha() ? cf.HSL : cf.HSLA;
+
+                        case cf.HSL:
+                        case cf.HSLA:
+                            if (color.nickname())
+                                return cf.Nickname;
+                            if (!color.hasAlpha())
+                                return color.canBeShortHex() ? cf.ShortHEX : cf.HEX;
+                            else
+                                return cf.Original;
+
+                        case cf.ShortHEX:
+                            return cf.HEX;
+
+                        case cf.HEX:
+                            return cf.Original;
+
+                        case cf.Nickname:
+                            if (!color.hasAlpha())
+                                return color.canBeShortHex() ? cf.ShortHEX : cf.HEX;
+                            else
+                                return cf.Original;
+
+                        default:
+                            return cf.RGBA;
+                    }
+                }
+
+                function changeColorDisplay(event)
+                {
+                    do {
+                        format = nextFormat(format);
+                        var currentValue = color.toString(format);
+                    } while (currentValue === colorValueElement.textContent);
+                    colorValueElement.textContent = currentValue;
+                }
+
+                var container = document.createElement("nobr");
+                container.appendChild(colorSwatch.element);
+                container.appendChild(colorValueElement);
+                return container;
+            }
+
+            var colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g;
+            var colorProcessor = processValue.bind(window, colorRegex, processColor, null);
+
+            valueElement.appendChild(processValue(/url\(\s*([^)]+)\s*\)/g, linkifyURL.bind(this), WebInspector.CSSMetadata.isColorAwareProperty(self.name) ? colorProcessor : null, value));
+        }
+
+        this.listItemElement.removeChildren();
+        nameElement.normalize();
+        valueElement.normalize();
+
+        if (!this.treeOutline)
+            return;
+
+        this.listItemElement.appendChild(nameElement);
+        this.listItemElement.appendChild(document.createTextNode(": "));
+        this.listItemElement.appendChild(this._expandElement);
+        this.listItemElement.appendChild(valueElement);
+        this.listItemElement.appendChild(document.createTextNode(";"));
+
+        if (!this.parsedOk) {
+            // Avoid having longhands under an invalid shorthand.
+            this.hasChildren = false;
+            this.listItemElement.addStyleClass("not-parsed-ok");
+
+            // Add a separate exclamation mark IMG element with a tooltip.
+            this.listItemElement.insertBefore(WebInspector.StylesSidebarPane.createExclamationMark(this.property.name), this.listItemElement.firstChild);
+        }
+        if (this.property.inactive)
+            this.listItemElement.addStyleClass("inactive");
+    },
+
+    updateState: function()
+    {
+        if (!this.listItemElement)
+            return;
+
+        if (this.style.isPropertyImplicit(this.name) || this.value === "initial")
+            this.listItemElement.addStyleClass("implicit");
+        else
+            this.listItemElement.removeStyleClass("implicit");
+
+        if (this.inherited)
+            this.listItemElement.addStyleClass("inherited");
+        else
+            this.listItemElement.removeStyleClass("inherited");
+
+        if (this.overloaded)
+            this.listItemElement.addStyleClass("overloaded");
+        else
+            this.listItemElement.removeStyleClass("overloaded");
+
+        if (this.disabled)
+            this.listItemElement.addStyleClass("disabled");
+        else
+            this.listItemElement.removeStyleClass("disabled");
+    },
+
+    __proto__: TreeElement.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StylePropertyTreeElementBase}
+ * @param {WebInspector.StylesSidebarPane} stylesPane
+ * @param {Object} styleRule
+ * @param {WebInspector.CSSStyleDeclaration} style
+ * @param {WebInspector.CSSProperty} property
+ * @param {boolean} inherited
+ */
+WebInspector.ComputedStylePropertyTreeElement = function(stylesPane, styleRule, style, property, inherited)
+{
+    WebInspector.StylePropertyTreeElementBase.call(this, styleRule, style, property, inherited, false, false);
+    this._stylesPane = stylesPane;
+}
+
+WebInspector.ComputedStylePropertyTreeElement.prototype = {
+    /**
+     * @return {?WebInspector.DOMNode}
+     */
+    node: function()
+    {
+        return this._stylesPane.node;
+    },
+
+    /**
+     * @return {?WebInspector.StylesSidebarPane}
+     */
+    editablePane: function()
+    {
+        return null;
+    },
+
+    __proto__: WebInspector.StylePropertyTreeElementBase.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.StylePropertyTreeElementBase}
+ * @param {?WebInspector.StylesSidebarPane} stylesPane
+ * @param {Object} styleRule
+ * @param {WebInspector.CSSStyleDeclaration} style
+ * @param {WebInspector.CSSProperty} property
+ * @param {boolean} isShorthand
+ * @param {boolean} inherited
+ * @param {boolean} overloaded
+ */
+WebInspector.StylePropertyTreeElement = function(stylesPane, styleRule, style, property, isShorthand, inherited, overloaded)
+{
+    WebInspector.StylePropertyTreeElementBase.call(this, styleRule, style, property, inherited, overloaded, isShorthand);
+    this._parentPane = stylesPane;
+    this.isShorthand = isShorthand;
+}
+
+WebInspector.StylePropertyTreeElement.prototype = {
+    /**
+     * @return {?WebInspector.DOMNode}
+     */
+    node: function()
+    {
+        return this._parentPane.node;
+    },
+
+    /**
+     * @return {?WebInspector.StylesSidebarPane}
+     */
+    editablePane: function()
+    {
+        return this._parentPane;
+    },
+
+    /**
+     * @return {WebInspector.StylePropertiesSection}
+     */
+    section: function()
+    {
+        return this.treeOutline && this.treeOutline.section;
+    },
+
+    _updatePane: function(userCallback)
+    {
+        var section = this.section();
+        if (section && section.pane)
+            section.pane._refreshUpdate(section, false, userCallback);
+        else  {
+            if (userCallback)
+                userCallback();
+        }
+    },
+
+    toggleEnabled: function(event)
+    {
+        var disabled = !event.target.checked;
+
+        function callback(newStyle)
+        {
+            if (!newStyle)
+                return;
+
+            newStyle.parentRule = this.style.parentRule;
+            this.style = newStyle;
+            this._styleRule.style = newStyle;
+
+            var section = this.section();
+            if (section && section.pane)
+                section.pane.dispatchEventToListeners("style property toggled");
+
+            this._updatePane();
+
+            delete this._parentPane._userOperation;
+        }
+
+        this._parentPane._userOperation = true;
+        this.property.setDisabled(disabled, callback.bind(this));
+        event.consume();
+    },
+
+    onpopulate: function()
+    {
+        // Only populate once and if this property is a shorthand.
+        if (this.children.length || !this.isShorthand)
+            return;
+
+        var longhandProperties = this.style.longhandProperties(this.name);
+        for (var i = 0; i < longhandProperties.length; ++i) {
+            var name = longhandProperties[i].name;
+
+            var section = this.section();
+            if (section) {
+                var inherited = section.isPropertyInherited(name);
+                var overloaded = section.isPropertyOverloaded(name);
+            }
+
+            var liveProperty = this.style.getLiveProperty(name);
+            if (!liveProperty)
+                continue;
+
+            var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this._styleRule, this.style, liveProperty, false, inherited, overloaded);
+            this.appendChild(item);
+        }
+    },
+
+    restoreNameElement: function()
+    {
+        // Restore <span class="webkit-css-property"> if it doesn't yet exist or was accidentally deleted.
+        if (this.nameElement === this.listItemElement.querySelector(".webkit-css-property"))
+            return;
+
+        this.nameElement = document.createElement("span");
+        this.nameElement.className = "webkit-css-property";
+        this.nameElement.textContent = "";
+        this.listItemElement.insertBefore(this.nameElement, this.listItemElement.firstChild);
+    },
+
+    onattach: function()
+    {
+        WebInspector.StylePropertyTreeElementBase.prototype.onattach.call(this);
+
+        this.listItemElement.addEventListener("mousedown", this._mouseDown.bind(this));
+        this.listItemElement.addEventListener("mouseup", this._resetMouseDownElement.bind(this));
+        this.listItemElement.addEventListener("click", this._mouseClick.bind(this));
+    },
+
+    _mouseDown: function(event)
+    {
+        if (this._parentPane) {
+            this._parentPane._mouseDownTreeElement = this;
+            this._parentPane._mouseDownTreeElementIsName = this._isNameElement(event.target);
+            this._parentPane._mouseDownTreeElementIsValue = this._isValueElement(event.target);
+        }
+    },
+
+    _resetMouseDownElement: function()
+    {
+        if (this._parentPane) {
+            delete this._parentPane._mouseDownTreeElement;
+            delete this._parentPane._mouseDownTreeElementIsName;
+            delete this._parentPane._mouseDownTreeElementIsValue;
+        }
+    },
+
+    updateTitle: function()
+    {
+        WebInspector.StylePropertyTreeElementBase.prototype.updateTitle.call(this);
+
+        if (this.parsedOk && this.section() && this.parent.root) {
+            var enabledCheckboxElement = document.createElement("input");
+            enabledCheckboxElement.className = "enabled-button";
+            enabledCheckboxElement.type = "checkbox";
+            enabledCheckboxElement.checked = !this.disabled;
+            enabledCheckboxElement.addEventListener("click", this.toggleEnabled.bind(this), false);
+            this.listItemElement.insertBefore(enabledCheckboxElement, this.listItemElement.firstChild);
+        }
+    },
+
+    _mouseClick: function(event)
+    {
+        if (!window.getSelection().isCollapsed)
+            return;
+
+        event.consume(true);
+
+        if (event.target === this.listItemElement) {
+            var section = this.section();
+            if (!section || !section.editable)
+                return;
+
+            if (section._checkWillCancelEditing())
+                return;
+            section.addNewBlankProperty(this.property.index + 1).startEditing();
+            return;
+        }
+
+        if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && this.section().navigable) {
+            this._navigateToSource(event.target);
+            return;
+        }
+
+        this.startEditing(event.target);
+    },
+
+    /**
+     * @param {Element} element
+     */
+    _navigateToSource: function(element)
+    {
+        console.assert(this.section().navigable);
+        var propertyNameClicked = element === this.nameElement;
+        var uiLocation = this.property.uiLocation(propertyNameClicked);
+        if (!uiLocation)
+            return;
+        WebInspector.showPanel("scripts").showUISourceCode(uiLocation.uiSourceCode, uiLocation.lineNumber);
+    },
+
+    _isNameElement: function(element)
+    {
+        return element.enclosingNodeOrSelfWithClass("webkit-css-property") === this.nameElement;
+    },
+
+    _isValueElement: function(element)
+    {
+        return !!element.enclosingNodeOrSelfWithClass("value");
+    },
+
+    startEditing: function(selectElement)
+    {
+        // FIXME: we don't allow editing of longhand properties under a shorthand right now.
+        if (this.parent.isShorthand)
+            return;
+
+        if (selectElement === this._expandElement)
+            return;
+
+        var section = this.section();
+        if (section && !section.editable)
+            return;
+
+        if (!selectElement)
+            selectElement = this.nameElement; // No arguments passed in - edit the name element by default.
+        else
+            selectElement = selectElement.enclosingNodeOrSelfWithClass("webkit-css-property") || selectElement.enclosingNodeOrSelfWithClass("value");
+
+        var isEditingName = selectElement === this.nameElement;
+        if (!isEditingName) {
+            if (selectElement !== this.valueElement) {
+                // Click in the LI - start editing value.
+                selectElement = this.valueElement;
+            }
+
+            this.valueElement.textContent = this.value;
+        }
+
+        if (WebInspector.isBeingEdited(selectElement))
+            return;
+
+        var context = {
+            expanded: this.expanded,
+            hasChildren: this.hasChildren,
+            isEditingName: isEditingName,
+            previousContent: selectElement.textContent
+        };
+
+        // Lie about our children to prevent expanding on double click and to collapse shorthands.
+        this.hasChildren = false;
+
+        if (selectElement.parentElement)
+            selectElement.parentElement.addStyleClass("child-editing");
+        selectElement.textContent = selectElement.textContent; // remove color swatch and the like
+
+        function pasteHandler(context, event)
+        {
+            var data = event.clipboardData.getData("Text");
+            if (!data)
+                return;
+            var colonIdx = data.indexOf(":");
+            if (colonIdx < 0)
+                return;
+            var name = data.substring(0, colonIdx).trim();
+            var value = data.substring(colonIdx + 1).trim();
+
+            event.preventDefault();
+
+            if (!("originalName" in context)) {
+                context.originalName = this.nameElement.textContent;
+                context.originalValue = this.valueElement.textContent;
+            }
+            this.property.name = name;
+            this.property.value = value;
+            this.nameElement.textContent = name;
+            this.valueElement.textContent = value;
+            this.nameElement.normalize();
+            this.valueElement.normalize();
+
+            this.editingCommitted(null, event.target.textContent, context.previousContent, context, "forward");
+        }
+
+        function blurListener(context, event)
+        {
+            var treeElement = this._parentPane._mouseDownTreeElement;
+            var moveDirection = "";
+            if (treeElement === this) {
+                if (isEditingName && this._parentPane._mouseDownTreeElementIsValue)
+                    moveDirection = "forward";
+                if (!isEditingName && this._parentPane._mouseDownTreeElementIsName)
+                    moveDirection = "backward";
+            }
+            this.editingCommitted(null, event.target.textContent, context.previousContent, context, moveDirection);
+        }
+
+        delete this.originalPropertyText;
+
+        this._parentPane._isEditingStyle = true;
+        if (selectElement.parentElement)
+            selectElement.parentElement.scrollIntoViewIfNeeded(false);
+
+        var applyItemCallback = !isEditingName ? this._applyFreeFlowStyleTextEdit.bind(this, true) : undefined;
+        this._prompt = new WebInspector.StylesSidebarPane.CSSPropertyPrompt(isEditingName ? WebInspector.CSSMetadata.cssPropertiesMetainfo : WebInspector.CSSMetadata.keywordsForProperty(this.nameElement.textContent), this, isEditingName);
+        if (applyItemCallback) {
+            this._prompt.addEventListener(WebInspector.TextPrompt.Events.ItemApplied, applyItemCallback, this);
+            this._prompt.addEventListener(WebInspector.TextPrompt.Events.ItemAccepted, applyItemCallback, this);
+        }
+        var proxyElement = this._prompt.attachAndStartEditing(selectElement, blurListener.bind(this, context));
+
+        proxyElement.addEventListener("keydown", this.editingNameValueKeyDown.bind(this, context), false);
+        if (isEditingName)
+            proxyElement.addEventListener("paste", pasteHandler.bind(this, context));
+
+        window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1);
+    },
+
+    editingNameValueKeyDown: function(context, event)
+    {
+        if (event.handled)
+            return;
+
+        var isEditingName = context.isEditingName;
+        var result;
+
+        function shouldCommitValueSemicolon(text, cursorPosition)
+        {
+            // FIXME: should this account for semicolons inside comments?
+            var openQuote = "";
+            for (var i = 0; i < cursorPosition; ++i) {
+                var ch = text[i];
+                if (ch === "\\" && openQuote !== "")
+                    ++i; // skip next character inside string
+                else if (!openQuote && (ch === "\"" || ch === "'"))
+                    openQuote = ch;
+                else if (openQuote === ch)
+                    openQuote = "";
+            }
+            return !openQuote;
+        }
+
+        // FIXME: the ":"/";" detection does not work for non-US layouts due to the event being keydown rather than keypress.
+        var isFieldInputTerminated = (event.keyCode === WebInspector.KeyboardShortcut.Keys.Semicolon.code) &&
+            (isEditingName ? event.shiftKey : (!event.shiftKey && shouldCommitValueSemicolon(event.target.textContent, event.target.selectionLeftOffset())));
+        if (isEnterKey(event) || isFieldInputTerminated) {
+            // Enter or colon (for name)/semicolon outside of string (for value).
+            event.preventDefault();
+            result = "forward";
+        } else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
+            result = "cancel";
+        else if (!isEditingName && this._newProperty && event.keyCode === WebInspector.KeyboardShortcut.Keys.Backspace.code) {
+            // For a new property, when Backspace is pressed at the beginning of new property value, move back to the property name.
+            var selection = window.getSelection();
+            if (selection.isCollapsed && !selection.focusOffset) {
+                event.preventDefault();
+                result = "backward";
+            }
+        } else if (event.keyIdentifier === "U+0009") { // Tab key.
+            result = event.shiftKey ? "backward" : "forward";
+            event.preventDefault();
+        }
+
+        if (result) {
+            switch (result) {
+            case "cancel":
+                this.editingCancelled(null, context);
+                break;
+            case "forward":
+            case "backward":
+                this.editingCommitted(null, event.target.textContent, context.previousContent, context, result);
+                break;
+            }
+
+            event.consume();
+            return;
+        }
+
+        if (!isEditingName)
+            this._applyFreeFlowStyleTextEdit(false);
+    },
+
+    _applyFreeFlowStyleTextEdit: function(now)
+    {
+        if (this._applyFreeFlowStyleTextEditTimer)
+            clearTimeout(this._applyFreeFlowStyleTextEditTimer);
+
+        function apply()
+        {
+            var valueText = this.valueElement.textContent;
+            if (valueText.indexOf(";") === -1)
+                this.applyStyleText(this.nameElement.textContent + ": " + valueText, false, false, false);
+        }
+        if (now)
+            apply.call(this);
+        else
+            this._applyFreeFlowStyleTextEditTimer = setTimeout(apply.bind(this), 100);
+    },
+
+    kickFreeFlowStyleEditForTest: function()
+    {
+        this._applyFreeFlowStyleTextEdit(true);
+    },
+
+    editingEnded: function(context)
+    {
+        this._resetMouseDownElement();
+        if (this._applyFreeFlowStyleTextEditTimer)
+            clearTimeout(this._applyFreeFlowStyleTextEditTimer);
+
+        this.hasChildren = context.hasChildren;
+        if (context.expanded)
+            this.expand();
+        var editedElement = context.isEditingName ? this.nameElement : this.valueElement;
+        // The proxyElement has been deleted, no need to remove listener.
+        if (editedElement.parentElement)
+            editedElement.parentElement.removeStyleClass("child-editing");
+
+        delete this._parentPane._isEditingStyle;
+    },
+
+    editingCancelled: function(element, context)
+    {
+        this._removePrompt();
+        this._revertStyleUponEditingCanceled(this.originalPropertyText);
+        // This should happen last, as it clears the info necessary to restore the property value after [Page]Up/Down changes.
+        this.editingEnded(context);
+    },
+
+    _revertStyleUponEditingCanceled: function(originalPropertyText)
+    {
+        if (typeof originalPropertyText === "string") {
+            delete this.originalPropertyText;
+            this.applyStyleText(originalPropertyText, true, false, true);
+        } else {
+            if (this._newProperty)
+                this.treeOutline.removeChild(this);
+            else
+                this.updateTitle();
+        }
+    },
+
+    _findSibling: function(moveDirection)
+    {
+        var target = this;
+        do {
+            target = (moveDirection === "forward" ? target.nextSibling : target.previousSibling);
+        } while(target && target.inherited);
+
+        return target;
+    },
+
+    editingCommitted: function(element, userInput, previousContent, context, moveDirection)
+    {
+        this._removePrompt();
+        this.editingEnded(context);
+        var isEditingName = context.isEditingName;
+
+        // Determine where to move to before making changes
+        var createNewProperty, moveToPropertyName, moveToSelector;
+        var isDataPasted = "originalName" in context;
+        var isDirtyViaPaste = isDataPasted && (this.nameElement.textContent !== context.originalName || this.valueElement.textContent !== context.originalValue);
+        var isPropertySplitPaste = isDataPasted && isEditingName && this.valueElement.textContent !== context.originalValue;
+        var moveTo = this;
+        var moveToOther = (isEditingName ^ (moveDirection === "forward"));
+        var abandonNewProperty = this._newProperty && !userInput && (moveToOther || isEditingName);
+        if (moveDirection === "forward" && (!isEditingName || isPropertySplitPaste) || moveDirection === "backward" && isEditingName) {
+            moveTo = moveTo._findSibling(moveDirection);
+            if (moveTo)
+                moveToPropertyName = moveTo.name;
+            else if (moveDirection === "forward" && (!this._newProperty || userInput))
+                createNewProperty = true;
+            else if (moveDirection === "backward")
+                moveToSelector = true;
+        }
+
+        // Make the Changes and trigger the moveToNextCallback after updating.
+        var moveToIndex = moveTo && this.treeOutline ? this.treeOutline.children.indexOf(moveTo) : -1;
+        var blankInput = /^\s*$/.test(userInput);
+        var shouldCommitNewProperty = this._newProperty && (isPropertySplitPaste || moveToOther || (!moveDirection && !isEditingName) || (isEditingName && blankInput));
+        var section = this.section();
+        if (((userInput !== previousContent || isDirtyViaPaste) && !this._newProperty) || shouldCommitNewProperty) {
+            section._afterUpdate = moveToNextCallback.bind(this, this._newProperty, !blankInput, section);
+            var propertyText;
+            if (blankInput || (this._newProperty && /^\s*$/.test(this.valueElement.textContent)))
+                propertyText = "";
+            else {
+                if (isEditingName)
+                    propertyText = userInput + ": " + this.valueElement.textContent;
+                else
+                    propertyText = this.nameElement.textContent + ": " + userInput;
+            }
+            this.applyStyleText(propertyText, true, true, false);
+        } else {
+            if (!isDataPasted && !this._newProperty)
+                this.updateTitle();
+            moveToNextCallback.call(this, this._newProperty, false, section);
+        }
+
+        // The Callback to start editing the next/previous property/selector.
+        function moveToNextCallback(alreadyNew, valueChanged, section)
+        {
+            if (!moveDirection)
+                return;
+
+            // User just tabbed through without changes.
+            if (moveTo && moveTo.parent) {
+                moveTo.startEditing(!isEditingName ? moveTo.nameElement : moveTo.valueElement);
+                return;
+            }
+
+            // User has made a change then tabbed, wiping all the original treeElements.
+            // Recalculate the new treeElement for the same property we were going to edit next.
+            if (moveTo && !moveTo.parent) {
+                var propertyElements = section.propertiesTreeOutline.children;
+                if (moveDirection === "forward" && blankInput && !isEditingName)
+                    --moveToIndex;
+                if (moveToIndex >= propertyElements.length && !this._newProperty)
+                    createNewProperty = true;
+                else {
+                    var treeElement = moveToIndex >= 0 ? propertyElements[moveToIndex] : null;
+                    if (treeElement) {
+                        var elementToEdit = !isEditingName || isPropertySplitPaste ? treeElement.nameElement : treeElement.valueElement;
+                        if (alreadyNew && blankInput)
+                            elementToEdit = moveDirection === "forward" ? treeElement.nameElement : treeElement.valueElement;
+                        treeElement.startEditing(elementToEdit);
+                        return;
+                    } else if (!alreadyNew)
+                        moveToSelector = true;
+                }
+            }
+
+            // Create a new attribute in this section (or move to next editable selector if possible).
+            if (createNewProperty) {
+                if (alreadyNew && !valueChanged && (isEditingName ^ (moveDirection === "backward")))
+                    return;
+
+                section.addNewBlankProperty().startEditing();
+                return;
+            }
+
+            if (abandonNewProperty) {
+                moveTo = this._findSibling(moveDirection);
+                var sectionToEdit = (moveTo || moveDirection === "backward") ? section : section.nextEditableSibling();
+                if (sectionToEdit) {
+                    if (sectionToEdit.rule)
+                        sectionToEdit.startEditingSelector();
+                    else
+                        sectionToEdit._moveEditorFromSelector(moveDirection);
+                }
+                return;
+            }
+
+            if (moveToSelector) {
+                if (section.rule)
+                    section.startEditingSelector();
+                else
+                    section._moveEditorFromSelector(moveDirection);
+            }
+        }
+    },
+
+    _removePrompt: function()
+    {
+        // BUG 53242. This cannot go into editingEnded(), as it should always happen first for any editing outcome.
+        if (this._prompt) {
+            this._prompt.detach();
+            delete this._prompt;
+        }
+    },
+
+    _hasBeenModifiedIncrementally: function()
+    {
+        // New properties applied via up/down or live editing have an originalPropertyText and will be deleted later
+        // on, if cancelled, when the empty string gets applied as their style text.
+        return typeof this.originalPropertyText === "string" || (!!this.property.propertyText && this._newProperty);
+    },
+
+    applyStyleText: function(styleText, updateInterface, majorChange, isRevert)
+    {
+        function userOperationFinishedCallback(parentPane, updateInterface)
+        {
+            if (updateInterface)
+                delete parentPane._userOperation;
+        }
+
+        // Leave a way to cancel editing after incremental changes.
+        if (!isRevert && !updateInterface && !this._hasBeenModifiedIncrementally()) {
+            // Remember the rule's original CSS text on [Page](Up|Down), so it can be restored
+            // if the editing is canceled.
+            this.originalPropertyText = this.property.propertyText;
+        }
+
+        if (!this.treeOutline)
+            return;
+
+        var section = this.section();
+        styleText = styleText.replace(/\s/g, " ").trim(); // Replace &nbsp; with whitespace.
+        var styleTextLength = styleText.length;
+        if (!styleTextLength && updateInterface && !isRevert && this._newProperty && !this._hasBeenModifiedIncrementally()) {
+            // The user deleted everything and never applied a new property value via Up/Down scrolling/live editing, so remove the tree element and update.
+            this.parent.removeChild(this);
+            section.afterUpdate();
+            return;
+        }
+
+        var currentNode = this._parentPane.node;
+        if (updateInterface)
+            this._parentPane._userOperation = true;
+
+        function callback(userCallback, originalPropertyText, newStyle)
+        {
+            if (!newStyle) {
+                if (updateInterface) {
+                    // It did not apply, cancel editing.
+                    this._revertStyleUponEditingCanceled(originalPropertyText);
+                }
+                userCallback();
+                return;
+            }
+
+            if (this._newProperty)
+                this._newPropertyInStyle = true;
+            newStyle.parentRule = this.style.parentRule;
+            this.style = newStyle;
+            this.property = newStyle.propertyAt(this.property.index);
+            this._styleRule.style = this.style;
+
+            if (section && section.pane)
+                section.pane.dispatchEventToListeners("style edited");
+
+            if (updateInterface && currentNode === this.node()) {
+                this._updatePane(userCallback);
+                return;
+            }
+
+            userCallback();
+        }
+
+        // Append a ";" if the new text does not end in ";".
+        // FIXME: this does not handle trailing comments.
+        if (styleText.length && !/;\s*$/.test(styleText))
+            styleText += ";";
+        var overwriteProperty = !!(!this._newProperty || this._newPropertyInStyle);
+        this.property.setText(styleText, majorChange, overwriteProperty, callback.bind(this, userOperationFinishedCallback.bind(null, this._parentPane, updateInterface), this.originalPropertyText));
+    },
+
+    ondblclick: function()
+    {
+        return true; // handled
+    },
+
+    isEventWithinDisclosureTriangle: function(event)
+    {
+        return event.target === this._expandElement;
+    },
+
+    __proto__: WebInspector.StylePropertyTreeElementBase.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TextPrompt}
+ * @param {!WebInspector.CSSMetadata} cssCompletions
+ * @param {!WebInspector.StylePropertyTreeElement} sidebarPane
+ * @param {boolean} isEditingName
+ */
+WebInspector.StylesSidebarPane.CSSPropertyPrompt = function(cssCompletions, sidebarPane, isEditingName)
+{
+    // Use the same callback both for applyItemCallback and acceptItemCallback.
+    WebInspector.TextPrompt.call(this, this._buildPropertyCompletions.bind(this), WebInspector.StyleValueDelimiters);
+    this.setSuggestBoxEnabled("generic-suggest");
+    this._cssCompletions = cssCompletions;
+    this._sidebarPane = sidebarPane;
+    this._isEditingName = isEditingName;
+}
+
+WebInspector.StylesSidebarPane.CSSPropertyPrompt.prototype = {
+    onKeyDown: function(event)
+    {
+        switch (event.keyIdentifier) {
+        case "Up":
+        case "Down":
+        case "PageUp":
+        case "PageDown":
+            if (this._handleNameOrValueUpDown(event)) {
+                event.preventDefault();
+                return;
+            }
+            break;
+        }
+
+        WebInspector.TextPrompt.prototype.onKeyDown.call(this, event);
+    },
+
+    onMouseWheel: function(event)
+    {
+        if (this._handleNameOrValueUpDown(event)) {
+            event.consume(true);
+            return;
+        }
+        WebInspector.TextPrompt.prototype.onMouseWheel.call(this, event);
+    },
+
+    tabKeyPressed: function()
+    {
+        this.acceptAutoComplete();
+
+        // Always tab to the next field.
+        return false;
+    },
+
+    _handleNameOrValueUpDown: function(event)
+    {
+        function finishHandler(originalValue, replacementString)
+        {
+            // Synthesize property text disregarding any comments, custom whitespace etc.
+            this._sidebarPane.applyStyleText(this._sidebarPane.nameElement.textContent + ": " + this._sidebarPane.valueElement.textContent, false, false, false);
+        }
+
+        // Handle numeric value increment/decrement only at this point.
+        if (!this._isEditingName && WebInspector.handleElementValueModifications(event, this._sidebarPane.valueElement, finishHandler.bind(this), this._isValueSuggestion.bind(this)))
+            return true;
+
+        return false;
+    },
+
+    _isValueSuggestion: function(word)
+    {
+        if (!word)
+            return false;
+        word = word.toLowerCase();
+        return this._cssCompletions.keySet().hasOwnProperty(word);
+    },
+
+    /**
+     * @param {Element} proxyElement
+     * @param {Range} wordRange
+     * @param {boolean} force
+     * @param {function(!Array.<string>, number=)} completionsReadyCallback
+     */
+    _buildPropertyCompletions: function(proxyElement, wordRange, force, completionsReadyCallback)
+    {
+        var prefix = wordRange.toString().toLowerCase();
+        if (!prefix && !force)
+            return;
+
+        var results = this._cssCompletions.startsWith(prefix);
+        var selectedIndex = this._cssCompletions.mostUsedOf(results);
+        completionsReadyCallback(results, selectedIndex);
+    },
+
+    __proto__: WebInspector.TextPrompt.prototype
+}
diff --git a/Source/devtools/front_end/StylesSourceMapping.js b/Source/devtools/front_end/StylesSourceMapping.js
new file mode 100644
index 0000000..7fe246d
--- /dev/null
+++ b/Source/devtools/front_end/StylesSourceMapping.js
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @implements {WebInspector.SourceMapping}
+ * @param {WebInspector.CSSStyleModel} cssModel
+ * @param {WebInspector.Workspace} workspace
+ */
+WebInspector.StylesSourceMapping = function(cssModel, workspace)
+{
+    this._cssModel = cssModel;
+    this._workspace = workspace;
+    this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset, this);
+    this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this);
+
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameCreatedOrNavigated, this._mainFrameCreatedOrNavigated, this);
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this);
+    this._initialize();
+}
+
+WebInspector.StylesSourceMapping.prototype = {
+    /**
+     * @param {WebInspector.RawLocation} rawLocation
+     * @return {WebInspector.UILocation}
+     */
+    rawLocationToUILocation: function(rawLocation)
+    {
+        var location = /** @type WebInspector.CSSLocation */ (rawLocation);
+        var uiSourceCode = this._workspace.uiSourceCodeForURL(location.url);
+        return new WebInspector.UILocation(uiSourceCode, location.lineNumber, 0);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.RawLocation}
+     */
+    uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
+    {
+        return new WebInspector.CSSLocation(uiSourceCode.url || "", lineNumber);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isIdentity: function()
+    {
+        return true;
+    },
+
+    _resourceAdded: function(event)
+    {
+        var resource = /** @type {WebInspector.UISourceCode} */ (event.data);
+        if (resource.contentType() !== WebInspector.resourceTypes.Stylesheet)
+            return;
+        if (!resource.url)
+            return;
+        var uiSourceCode = this._workspace.uiSourceCodeForURL(resource.url);
+        if (!uiSourceCode)
+            return;
+        this._bindUISourceCode(uiSourceCode);
+    },
+
+    _uiSourceCodeAddedToWorkspace: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
+        if (uiSourceCode.contentType() !== WebInspector.resourceTypes.Stylesheet)
+            return;
+        if (!uiSourceCode.url || !WebInspector.resourceForURL(uiSourceCode.url))
+            return;
+        this._bindUISourceCode(uiSourceCode);
+    },
+
+    _bindUISourceCode: function(uiSourceCode)
+    {
+        if (this._mappedURLs[uiSourceCode.url])
+            return;
+        this._mappedURLs[uiSourceCode.url] = true;
+        uiSourceCode.setSourceMapping(this);
+        var styleFile = new WebInspector.StyleFile(uiSourceCode);
+        uiSourceCode.setStyleFile(styleFile);
+        this._cssModel.setSourceMapping(uiSourceCode.url, this);
+    },
+
+    _projectWillReset: function(event)
+    {
+        var project = event.data;
+        var uiSourceCodes = project.uiSourceCodes();
+        for (var i = 0; i < uiSourceCodes; ++i)
+            delete this._mappedURLs[uiSourceCodes[i].url];
+    },
+
+    _initialize: function()
+    {
+        /** {Object.<string, boolean>} */
+        this._mappedURLs = {};
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _mainFrameCreatedOrNavigated: function(event)
+    {
+        for (var mappedURL in this._mappedURLs) {
+            var uiSourceCode = this._workspace.uiSourceCodeForURL(mappedURL);
+            if (!uiSourceCode)
+                continue;
+            uiSourceCode.styleFile().dispose();
+            uiSourceCode.setStyleFile(null);
+            uiSourceCode.setSourceMapping(null);
+        }
+        this._initialize();
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ */
+WebInspector.StyleFile = function(uiSourceCode)
+{
+    this._uiSourceCode = uiSourceCode;
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+}
+
+WebInspector.StyleFile.updateTimeout = 200;
+
+WebInspector.StyleFile.prototype = {
+    _workingCopyCommitted: function(event)
+    {
+        if (this._isAddingRevision)
+            return;
+
+        this._commitIncrementalEdit(true);
+    },
+
+    _workingCopyChanged: function(event)
+    {
+        if (this._isAddingRevision)
+            return;
+
+        // FIXME: Extensions tests override updateTimeout because extensions don't have any control over applying changes to domain specific bindings.
+        if (WebInspector.StyleFile.updateTimeout >= 0) {
+            this._incrementalUpdateTimer = setTimeout(this._commitIncrementalEdit.bind(this, false), WebInspector.StyleFile.updateTimeout)
+        } else
+            this._commitIncrementalEdit(false);
+    },
+
+    /**
+     * @param {boolean} majorChange
+     */
+    _commitIncrementalEdit: function(majorChange)
+    {
+        this._clearIncrementalUpdateTimer();
+        WebInspector.styleContentBinding.setStyleContent(this._uiSourceCode, this._uiSourceCode.workingCopy(), majorChange, this._styleContentSet.bind(this));
+    },
+
+    /**
+     * @param {?string} error
+     */
+    _styleContentSet: function(error)
+    {
+        if (error)
+            WebInspector.showErrorMessage(error);
+    },
+
+    _clearIncrementalUpdateTimer: function()
+    {
+        if (!this._incrementalUpdateTimer)
+            return;
+        clearTimeout(this._incrementalUpdateTimer);
+        delete this._incrementalUpdateTimer;
+    },
+
+    /**
+     * @param {string} content
+     */
+    addRevision: function(content)
+    {
+        this._isAddingRevision = true;
+        this._uiSourceCode.addRevision(content);
+        delete this._isAddingRevision;
+    },
+
+    dispose: function()
+    {
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
+        this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.CSSStyleModel} cssModel
+ */
+WebInspector.StyleContentBinding = function(cssModel, workspace)
+{
+    this._cssModel = cssModel;
+    this._workspace = workspace;
+    this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetChanged, this);
+}
+
+WebInspector.StyleContentBinding.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {string} content
+     * @param {boolean} majorChange
+     * @param {function(?string)} userCallback
+     */
+    setStyleContent: function(uiSourceCode, content, majorChange, userCallback)
+    {
+        var resource = WebInspector.resourceForURL(uiSourceCode.url);
+        if (!resource) {
+            userCallback("No resource found: " + uiSourceCode.url);
+            return;
+        }
+
+        var styleSheetId = this._cssModel.resourceBinding().styleSheetIdForResource(resource);
+        if (!styleSheetId) {
+            userCallback("No stylesheet found: " + resource.frameId + ":" + resource.url);
+            return;
+        }
+
+        this._isSettingContent = true;
+        function callback(error)
+        {
+            userCallback(error);
+            delete this._isSettingContent;
+        }
+        this._cssModel.setStyleSheetText(styleSheetId, content, majorChange, callback.bind(this));
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _styleSheetChanged: function(event)
+    {
+        if (this._isSettingContent)
+            return;
+
+        if (!event.data.majorChange)
+            return;
+
+        /**
+         * @param {?string} error
+         * @param {string} content
+         */
+        function callback(error, content)
+        {
+            if (!error)
+                this._innerStyleSheetChanged(event.data.styleSheetId, content);
+        }
+        CSSAgent.getStyleSheetText(event.data.styleSheetId, callback.bind(this));
+    },
+
+    /**
+     * @param {CSSAgent.StyleSheetId} styleSheetId
+     * @param {string} content
+     */
+    _innerStyleSheetChanged: function(styleSheetId, content)
+    {
+        var styleSheetURL = this._cssModel.resourceBinding().resourceURLForStyleSheetId(styleSheetId);
+        if (typeof styleSheetURL !== "string")
+            return;
+
+        var uiSourceCode = this._workspace.uiSourceCodeForURL(styleSheetURL)
+        if (!uiSourceCode)
+            return;
+
+        if (uiSourceCode.styleFile())
+            uiSourceCode.styleFile().addRevision(content);
+    }
+}
+
+/**
+ * @type {?WebInspector.StyleContentBinding}
+ */
+WebInspector.styleContentBinding = null;
diff --git a/Source/devtools/front_end/SuggestBox.js b/Source/devtools/front_end/SuggestBox.js
new file mode 100644
index 0000000..13a4e49
--- /dev/null
+++ b/Source/devtools/front_end/SuggestBox.js
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.SuggestBoxDelegate = function()
+{
+}
+
+WebInspector.SuggestBoxDelegate.prototype = {
+    /**
+     * @param {string} suggestion
+     * @param {boolean=} isIntermediateSuggestion
+     */
+    applySuggestion: function(suggestion, isIntermediateSuggestion) { },
+
+    /**
+     * acceptSuggestion will be always called after call to applySuggestion with isIntermediateSuggestion being equal to false.
+     */
+    acceptSuggestion: function() { },
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.SuggestBoxDelegate} suggestBoxDelegate
+ * @param {Element} anchorElement
+ * @param {string=} className
+ */
+WebInspector.SuggestBox = function(suggestBoxDelegate, anchorElement, className)
+{
+    this._suggestBoxDelegate = suggestBoxDelegate;
+    this._anchorElement = anchorElement;
+    this._length = 0;
+    this._selectedIndex = -1;
+    this._selectedElement = null;
+    this._boundOnScroll = this._onScrollOrResize.bind(this, true);
+    this._boundOnResize = this._onScrollOrResize.bind(this, false);
+    window.addEventListener("scroll", this._boundOnScroll, true);
+    window.addEventListener("resize", this._boundOnResize, true);
+
+    this._bodyElement = anchorElement.ownerDocument.body;
+    this._element = anchorElement.ownerDocument.createElement("div");
+    this._element.className = "suggest-box " + (className || "");
+    this._element.addEventListener("mousedown", this._onBoxMouseDown.bind(this), true);
+    this.containerElement = this._element.createChild("div", "container");
+    this.contentElement = this.containerElement.createChild("div", "content");
+}
+
+WebInspector.SuggestBox.prototype = {
+    /**
+     * @return {boolean}
+     */
+    visible: function()
+    {
+        return !!this._element.parentElement;
+    },
+
+    /**
+     * @param {boolean} isScroll
+     * @param {Event} event
+     */
+    _onScrollOrResize: function(isScroll, event)
+    {
+        if (isScroll && this._element.isAncestor(event.target) || !this.visible())
+            return;
+        this._updateBoxPosition(this._anchorBox);
+    },
+
+    /**
+     * @param {AnchorBox} anchorBox
+     */
+    _updateBoxPosition: function(anchorBox)
+    {
+        this._anchorBox = anchorBox;
+
+        // Measure the content element box.
+        this.contentElement.style.display = "inline-block";
+        document.body.appendChild(this.contentElement);
+        this.contentElement.positionAt(0, 0);
+        var contentWidth = this.contentElement.offsetWidth;
+        var contentHeight = this.contentElement.offsetHeight;
+        this.contentElement.style.display = "block";
+        this.containerElement.appendChild(this.contentElement);
+
+        const spacer = 6;
+        const suggestBoxPaddingX = 21;
+        const suggestBoxPaddingY = 2;
+
+        var maxWidth = document.body.offsetWidth - anchorBox.x - spacer;
+        var width = Math.min(contentWidth, maxWidth - suggestBoxPaddingX) + suggestBoxPaddingX;
+        var paddedWidth = contentWidth + suggestBoxPaddingX;
+        var boxX = anchorBox.x;
+        if (width < paddedWidth) {
+            // Shift the suggest box to the left to accommodate the content without trimming to the BODY edge.
+            maxWidth = document.body.offsetWidth - spacer;
+            width = Math.min(contentWidth, maxWidth - suggestBoxPaddingX) + suggestBoxPaddingX;
+            boxX = document.body.offsetWidth - width;
+        }
+
+        var boxY;
+        var aboveHeight = anchorBox.y;
+        var underHeight = document.body.offsetHeight - anchorBox.y - anchorBox.height;
+        var maxHeight = Math.max(underHeight, aboveHeight) - spacer;
+        var height = Math.min(contentHeight, maxHeight - suggestBoxPaddingY) + suggestBoxPaddingY;
+        if (underHeight >= aboveHeight) {
+            // Locate the suggest box under the anchorBox.
+            boxY = anchorBox.y + anchorBox.height;
+            this._element.removeStyleClass("above-anchor");
+            this._element.addStyleClass("under-anchor");
+        } else {
+            // Locate the suggest box above the anchorBox.
+            boxY = anchorBox.y - height;
+            this._element.removeStyleClass("under-anchor");
+            this._element.addStyleClass("above-anchor");
+        }
+
+        this._element.positionAt(boxX, boxY);
+        this._element.style.width = width + "px";
+        this._element.style.height = height + "px";
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onBoxMouseDown: function(event)
+    {
+        event.preventDefault();
+    },
+
+    hide: function()
+    {
+        if (!this.visible())
+            return;
+
+        this._element.parentElement.removeChild(this._element);
+        delete this._selectedElement;
+    },
+
+    removeFromElement: function()
+    {
+        window.removeEventListener("scroll", this._boundOnScroll, true);
+        window.removeEventListener("resize", this._boundOnResize, true);
+        this.hide();
+    },
+
+    /**
+     * @param {string=} text
+     * @param {boolean=} isIntermediateSuggestion
+     */
+    _applySuggestion: function(text, isIntermediateSuggestion)
+    {
+        if (!this.visible() || !(text || this._selectedElement))
+            return false;
+
+        var suggestion = text || this._selectedElement.textContent;
+        if (!suggestion)
+            return false;
+
+        this._suggestBoxDelegate.applySuggestion(suggestion, isIntermediateSuggestion);
+        return true;
+    },
+
+    /**
+     * @param {string=} text
+     */
+    acceptSuggestion: function(text)
+    {
+        var result = this._applySuggestion(text, false);
+        this.hide();
+        if (!result)
+            return false;
+
+        this._suggestBoxDelegate.acceptSuggestion();
+
+        return true;
+    },
+
+    /**
+     * @param {number} shift
+     * @param {boolean=} isCircular
+     * @return {boolean} is changed
+     */
+    _selectClosest: function(shift, isCircular)
+    {
+        if (!this._length)
+            return false;
+
+        var index = this._selectedIndex + shift;
+
+        if (isCircular)
+            index = (this._length + index) % this._length;
+        else
+            index = Number.constrain(index, 0, this._length - 1);
+
+        this._selectItem(index);
+        this._applySuggestion(undefined, true);
+        return true;
+    },
+
+    /**
+     * @param {string} text
+     * @param {Event} event
+     */
+    _onItemMouseDown: function(text, event)
+    {
+        this.acceptSuggestion(text);
+        event.consume(true);
+    },
+
+    /**
+     * @param {string} prefix
+     * @param {string} text
+     */
+    _createItemElement: function(prefix, text)
+    {
+        var element = document.createElement("div");
+        element.className = "suggest-box-content-item source-code";
+        element.tabIndex = -1;
+        if (prefix && prefix.length && !text.indexOf(prefix)) {
+            var prefixElement = element.createChild("span", "prefix");
+            prefixElement.textContent = prefix;
+            var suffixElement = element.createChild("span", "suffix");
+            suffixElement.textContent = text.substring(prefix.length);
+        } else {
+            var suffixElement = element.createChild("span", "suffix");
+            suffixElement.textContent = text;
+        }
+        element.addEventListener("mousedown", this._onItemMouseDown.bind(this, text), false);
+        return element;
+    },
+
+    /**
+     * @param {!Array.<string>} items
+     * @param {number} selectedIndex
+     * @param {string} userEnteredText
+     */
+    _updateItems: function(items, selectedIndex, userEnteredText)
+    {
+        this._length = items.length;
+        this.contentElement.removeChildren();
+
+        for (var i = 0; i < items.length; ++i) {
+            var item = items[i];
+            var currentItemElement = this._createItemElement(userEnteredText, item);
+            this.contentElement.appendChild(currentItemElement);
+        }
+
+        this._selectedElement = null;
+        if (typeof selectedIndex === "number")
+            this._selectItem(selectedIndex);
+    },
+
+    /**
+     * @param {number} index
+     */
+    _selectItem: function(index)
+    {
+        if (this._selectedElement)
+            this._selectedElement.classList.remove("selected");
+
+        this._selectedIndex = index;
+        this._selectedElement = this.contentElement.children[index];
+        this._selectedElement.classList.add("selected");
+
+        this._selectedElement.scrollIntoViewIfNeeded(false);
+    },
+
+    /**
+     * @param {!Array.<string>} completions
+     * @param {boolean} canShowForSingleItem
+     * @param {string} userEnteredText
+     */
+    _canShowBox: function(completions, canShowForSingleItem, userEnteredText)
+    {
+        if (!completions || !completions.length)
+            return false;
+
+        if (completions.length > 1)
+            return true;
+
+        // Do not show a single suggestion if it is the same as user-entered prefix, even if allowed to show single-item suggest boxes.
+        return canShowForSingleItem && completions[0] !== userEnteredText;
+    },
+
+    _rememberRowCountPerViewport: function()
+    {
+        if (!this.contentElement.firstChild)
+            return;
+
+        this._rowCountPerViewport = Math.floor(this.containerElement.offsetHeight / this.contentElement.firstChild.offsetHeight);
+    },
+
+    /**
+     * @param {AnchorBox} anchorBox
+     * @param {!Array.<string>} completions
+     * @param {number} selectedIndex
+     * @param {boolean} canShowForSingleItem
+     * @param {string} userEnteredText
+     */
+    updateSuggestions: function(anchorBox, completions, selectedIndex, canShowForSingleItem, userEnteredText)
+    {
+        if (this._canShowBox(completions, canShowForSingleItem, userEnteredText)) {
+            this._updateItems(completions, selectedIndex, userEnteredText);
+            this._updateBoxPosition(anchorBox);
+            if (!this.visible())
+                this._bodyElement.appendChild(this._element);
+            this._rememberRowCountPerViewport();
+        } else
+            this.hide();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    upKeyPressed: function()
+    {
+        return this._selectClosest(-1, true);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    downKeyPressed: function()
+    {
+        return this._selectClosest(1, true);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    pageUpKeyPressed: function()
+    {
+        return this._selectClosest(-this._rowCountPerViewport, false);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    pageDownKeyPressed: function()
+    {
+        return this._selectClosest(this._rowCountPerViewport, false);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    enterKeyPressed: function()
+    {
+        var hasSelectedItem = !!this._selectedElement;
+        this.acceptSuggestion();
+
+        // Report the event as non-handled if there is no selected item,
+        // to commit the input or handle it otherwise.
+        return hasSelectedItem;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    tabKeyPressed: function()
+    {
+        return this.enterKeyPressed();
+    }
+}
diff --git a/Source/devtools/front_end/TabbedEditorContainer.js b/Source/devtools/front_end/TabbedEditorContainer.js
new file mode 100644
index 0000000..98807c2
--- /dev/null
+++ b/Source/devtools/front_end/TabbedEditorContainer.js
@@ -0,0 +1,675 @@
+/*
+ * 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.TabbedEditorContainerDelegate = function() { }
+
+WebInspector.TabbedEditorContainerDelegate.prototype = {
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {WebInspector.SourceFrame}
+     */
+    viewForFile: function(uiSourceCode) { }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.TabbedEditorContainerDelegate} delegate
+ * @param {string} settingName
+ */
+WebInspector.TabbedEditorContainer = function(delegate, settingName)
+{
+    WebInspector.Object.call(this);
+    this._delegate = delegate;
+
+    this._tabbedPane = new WebInspector.TabbedPane();
+    this._tabbedPane.setTabDelegate(new WebInspector.EditorContainerTabDelegate(this));
+
+    this._tabbedPane.closeableTabs = true;
+    this._tabbedPane.element.id = "scripts-editor-container-tabbed-pane";
+
+    this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._tabClosed, this);
+    this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
+
+    this._tabIds = new Map();
+    this._files = {};
+    this._loadedURIs = {};
+
+    this._previouslyViewedFilesSetting = WebInspector.settings.createSetting(settingName, []);
+    this._history = WebInspector.TabbedEditorContainer.History.fromObject(this._previouslyViewedFilesSetting.get());
+}
+
+
+WebInspector.TabbedEditorContainer.Events = {
+    EditorSelected: "EditorSelected",
+    EditorClosed: "EditorClosed"
+}
+
+WebInspector.TabbedEditorContainer._tabId = 0;
+
+WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount = 30;
+
+WebInspector.TabbedEditorContainer.prototype = {
+    /**
+     * @return {WebInspector.View}
+     */
+    get view()
+    {
+        return this._tabbedPane;
+    },
+
+    /**
+     * @type {WebInspector.SourceFrame}
+     */
+    get visibleView()
+    {
+        return this._tabbedPane.visibleView;
+    },
+
+    /**
+     * @param {Element} parentElement
+     */
+    show: function(parentElement)
+    {
+        this._tabbedPane.show(parentElement);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    showFile: function(uiSourceCode)
+    {
+        this._innerShowFile(uiSourceCode, true);
+    },
+
+    _addScrollAndSelectionListeners: function()
+    {
+        if (!this._currentView)
+            return;
+        this._currentView.addEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this);
+        this._currentView.addEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this);
+    },
+
+    _removeScrollAndSelectionListeners: function()
+    {
+        if (!this._currentView)
+            return;
+        this._currentView.removeEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this);
+        this._currentView.removeEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this);
+    },
+
+    _scrollChanged: function(event)
+    {
+        var lineNumber = /** @type {number} */ (event.data);
+        this._history.updateScrollLineNumber(this._currentFile.uri(), lineNumber);
+        this._history.save(this._previouslyViewedFilesSetting);
+    },
+
+    _selectionChanged: function(event)
+    {
+        var range = /** @type {WebInspector.TextRange} */ (event.data);
+        this._history.updateSelectionRange(this._currentFile.uri(), range);
+        this._history.save(this._previouslyViewedFilesSetting);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {boolean=} userGesture
+     */
+    _innerShowFile: function(uiSourceCode, userGesture)
+    {
+        if (this._currentFile === uiSourceCode)
+            return;
+        this._removeScrollAndSelectionListeners();
+        this._currentFile = uiSourceCode;
+
+        var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCode, userGesture);
+        
+        this._tabbedPane.selectTab(tabId, userGesture);
+        if (userGesture)
+            this._editorSelectedByUserAction();
+        
+        this._currentView = this.visibleView;
+        this._addScrollAndSelectionListeners();
+        
+        this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorSelected, this._currentFile);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {string}
+     */
+    _titleForFile: function(uiSourceCode)
+    {
+        const maxDisplayNameLength = 30;
+        const minDisplayQueryParamLength = 5;
+
+        var title = uiSourceCode.name();
+        title = title ? title.trimMiddle(maxDisplayNameLength) : WebInspector.UIString("(program)");
+        if (uiSourceCode.isDirty())
+            title += "*";
+        return title;
+    },
+
+    /**
+     * @param {string} id
+     * @param {string} nextTabId
+     */
+    _maybeCloseTab: function(id, nextTabId)
+    {
+        var uiSourceCode = this._files[id];
+        var shouldPrompt = uiSourceCode.isDirty() && uiSourceCode.project().canSetFileContent();
+        // FIXME: this should be replaced with common Save/Discard/Cancel dialog.
+        if (!shouldPrompt || confirm(WebInspector.UIString("Are you sure you want to close unsaved file: %s?", uiSourceCode.name()))) {
+            uiSourceCode.resetWorkingCopy();
+            if (nextTabId)
+                this._tabbedPane.selectTab(nextTabId, true);
+            this._tabbedPane.closeTab(id, true);
+            return true;
+        }
+        return false;
+    },
+
+    /**
+     * @param {Array.<string>} ids
+     */
+    _closeTabs: function(ids)
+    {
+        var dirtyTabs = [];
+        var cleanTabs = [];
+        for (var i = 0; i < ids.length; ++i) {
+            var id = ids[i];
+            var uiSourceCode = this._files[id];
+            if (uiSourceCode.isDirty())
+                dirtyTabs.push(id);
+            else
+                cleanTabs.push(id);
+        }
+        if (dirtyTabs.length)
+            this._tabbedPane.selectTab(dirtyTabs[0], true);
+        this._tabbedPane.closeTabs(cleanTabs, true);
+        for (var i = 0; i < dirtyTabs.length; ++i) {
+            var nextTabId = i + 1 < dirtyTabs.length ? dirtyTabs[i + 1] : null;
+            if (!this._maybeCloseTab(dirtyTabs[i], nextTabId))
+                break;
+        }
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    addUISourceCode: function(uiSourceCode)
+    {
+        if (this._userSelectedFiles || this._loadedURIs[uiSourceCode.uri()])
+            return;
+        this._loadedURIs[uiSourceCode.uri()] = true;
+
+        var index = this._history.index(uiSourceCode.uri())
+        if (index === -1)
+            return;
+
+        var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCode, false);
+
+        if (!this._currentFile)
+            return;
+
+        // Select tab if this file was the last to be shown.
+        if (!index) {
+            this._innerShowFile(uiSourceCode, false);
+            return;
+        }
+
+        var currentProjectType = this._currentFile.project().type();
+        var addedProjectType = uiSourceCode.project().type();
+        var snippetsProjectType = WebInspector.projectTypes.Snippets;
+        if (this._history.index(this._currentFile.uri()) && currentProjectType === snippetsProjectType && addedProjectType !== snippetsProjectType)
+            this._innerShowFile(uiSourceCode, false);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    removeUISourceCode: function(uiSourceCode)
+    {
+        this.removeUISourceCodes([uiSourceCode]);
+    },
+
+    /**
+     * @param {Array.<WebInspector.UISourceCode>} uiSourceCodes
+     */
+    removeUISourceCodes: function(uiSourceCodes)
+    {
+        var tabIds = [];
+        for (var i = 0; i < uiSourceCodes.length; ++i) {
+            var uiSourceCode = uiSourceCodes[i];
+            delete this._loadedURIs[uiSourceCode.uri()];
+            var tabId = this._tabIds.get(uiSourceCode);
+            if (tabId)
+                tabIds.push(tabId);
+        }
+        this._tabbedPane.closeTabs(tabIds);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _editorClosedByUserAction: function(uiSourceCode)
+    {
+        this._userSelectedFiles = true;
+        this._history.remove(uiSourceCode.uri());
+        this._updateHistory();
+    },
+
+    _editorSelectedByUserAction: function()
+    {
+        this._userSelectedFiles = true;
+        this._updateHistory();
+    },
+
+    _updateHistory: function()
+    {
+        var tabIds = this._tabbedPane.lastOpenedTabIds(WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount);
+        
+        function tabIdToURI(tabId)
+        {
+            return this._files[tabId].uri();
+        }
+        
+        this._history.update(tabIds.map(tabIdToURI.bind(this)));
+        this._history.save(this._previouslyViewedFilesSetting);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @return {string}
+     */
+    _tooltipForFile: function(uiSourceCode)
+    {
+        return uiSourceCode.originURL();
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {boolean=} userGesture
+     */
+    _appendFileTab: function(uiSourceCode, userGesture)
+    {
+        var view = this._delegate.viewForFile(uiSourceCode);
+        var title = this._titleForFile(uiSourceCode);
+        var tooltip = this._tooltipForFile(uiSourceCode);
+
+        var tabId = this._generateTabId();
+        this._tabIds.put(uiSourceCode, tabId);
+        this._files[tabId] = uiSourceCode;
+
+        var savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.uri());
+        if (savedScrollLineNumber)
+            view.scrollToLine(savedScrollLineNumber);
+        var savedSelectionRange = this._history.selectionRange(uiSourceCode.uri());
+        if (savedSelectionRange)
+            view.setSelection(savedSelectionRange);
+
+        this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture);
+
+        this._addUISourceCodeListeners(uiSourceCode);
+        return tabId;
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _tabClosed: function(event)
+    {
+        var tabId = /** @type {string} */ (event.data.tabId);
+        var userGesture = /** @type {boolean} */ (event.data.isUserGesture);
+
+        var uiSourceCode = this._files[tabId];
+        if (this._currentFile === uiSourceCode) {
+            this._removeScrollAndSelectionListeners();
+            delete this._currentView;
+            delete this._currentFile;
+        }
+        this._tabIds.remove(uiSourceCode);
+        delete this._files[tabId];
+
+        this._removeUISourceCodeListeners(uiSourceCode);
+
+        this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorClosed, uiSourceCode);
+
+        if (userGesture)
+            this._editorClosedByUserAction(uiSourceCode);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _tabSelected: function(event)
+    {
+        var tabId = /** @type {string} */ (event.data.tabId);
+        var userGesture = /** @type {boolean} */ (event.data.isUserGesture);
+
+        var uiSourceCode = this._files[tabId];
+        this._innerShowFile(uiSourceCode, userGesture);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _addUISourceCodeListeners: function(uiSourceCode)
+    {
+        uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
+        uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
+        uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
+        uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormattedChanged, this);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _removeUISourceCodeListeners: function(uiSourceCode)
+    {
+        uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
+        uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
+        uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
+        uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormattedChanged, this);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    _updateFileTitle: function(uiSourceCode)
+    {
+        var tabId = this._tabIds.get(uiSourceCode);
+        if (tabId) {
+            var title = this._titleForFile(uiSourceCode);
+            this._tabbedPane.changeTabTitle(tabId, title);
+        }
+    },
+
+    _uiSourceCodeTitleChanged: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
+        this._updateFileTitle(uiSourceCode);
+    },
+
+    _uiSourceCodeWorkingCopyChanged: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
+        this._updateFileTitle(uiSourceCode);
+    },
+
+    _uiSourceCodeWorkingCopyCommitted: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
+        this._updateFileTitle(uiSourceCode);
+    },
+
+    _uiSourceCodeFormattedChanged: function(event)
+    {
+        var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
+        this._updateFileTitle(uiSourceCode);
+    },
+
+    reset: function()
+    {
+        delete this._userSelectedFiles;
+    },
+
+    /**
+     * @return {string}
+     */
+    _generateTabId: function()
+    {
+        return "tab_" + (WebInspector.TabbedEditorContainer._tabId++);
+    },
+
+    /**
+     * @return {WebInspector.UISourceCode} uiSourceCode
+     */
+    currentFile: function()
+    {
+        return this._currentFile;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {string} url
+ * @param {WebInspector.TextRange=} selectionRange
+ * @param {number=} scrollLineNumber
+ */
+WebInspector.TabbedEditorContainer.HistoryItem = function(url, selectionRange, scrollLineNumber)
+{
+    /** @const */ this.url = url;
+    /** @const */ this._isSerializable = url.length < WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit;
+    this.selectionRange = selectionRange;
+    this.scrollLineNumber = scrollLineNumber;
+}
+
+WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit = 4096;
+
+/**
+ * @param {Object} serializedHistoryItem
+ * @return {WebInspector.TabbedEditorContainer.HistoryItem}
+ */
+WebInspector.TabbedEditorContainer.HistoryItem.fromObject = function (serializedHistoryItem)
+{
+    var selectionRange = serializedHistoryItem.selectionRange ? WebInspector.TextRange.fromObject(serializedHistoryItem.selectionRange) : null;
+    return new WebInspector.TabbedEditorContainer.HistoryItem(serializedHistoryItem.url, selectionRange, serializedHistoryItem.scrollLineNumber);
+}
+
+WebInspector.TabbedEditorContainer.HistoryItem.prototype = {
+    /**
+     * @return {?Object}
+     */
+    serializeToObject: function()
+    {
+        if (!this._isSerializable)
+            return null;
+        var serializedHistoryItem = {};
+        serializedHistoryItem.url = this.url;
+        serializedHistoryItem.selectionRange = this.selectionRange;
+        serializedHistoryItem.scrollLineNumber = this.scrollLineNumber;
+        return serializedHistoryItem;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {Array.<WebInspector.TabbedEditorContainer.HistoryItem>} items
+ */
+WebInspector.TabbedEditorContainer.History = function(items)
+{
+    this._items = items;
+    this._rebuildItemIndex();
+}
+
+/**
+ * @param {!Array.<!Object>} serializedHistory
+ * @return {WebInspector.TabbedEditorContainer.History}
+ */
+WebInspector.TabbedEditorContainer.History.fromObject = function(serializedHistory)
+{
+    var items = [];
+    for (var i = 0; i < serializedHistory.length; ++i)
+        items.push(WebInspector.TabbedEditorContainer.HistoryItem.fromObject(serializedHistory[i]));
+    return new WebInspector.TabbedEditorContainer.History(items);
+}
+
+WebInspector.TabbedEditorContainer.History.prototype = {
+    /**
+     * @param {string} url
+     * @return {number}
+     */
+    index: function(url)
+    {
+        var index = this._itemsIndex[url];
+        if (typeof index === "number")
+            return index;
+        return -1;
+    },
+
+    _rebuildItemIndex: function()
+    {
+        this._itemsIndex = {};
+        for (var i = 0; i < this._items.length; ++i) {
+            console.assert(!this._itemsIndex.hasOwnProperty(this._items[i].url));
+            this._itemsIndex[this._items[i].url] = i;
+        }
+    },
+
+    /**
+     * @param {string} url
+     * @return {WebInspector.TextRange|undefined}
+     */
+    selectionRange: function(url)
+    {
+        var index = this.index(url);
+        return index !== -1 ? this._items[index].selectionRange : undefined;
+    },
+
+    /**
+     * @param {string} url
+     * @param {WebInspector.TextRange} selectionRange
+     */
+    updateSelectionRange: function(url, selectionRange)
+    {
+        if (!selectionRange)
+            return;
+        var index = this.index(url);
+        if (index === -1)
+            return;
+        this._items[index].selectionRange = selectionRange;
+    },
+
+    /**
+     * @param {string} url
+     * @return {number|undefined}
+     */
+    scrollLineNumber: function(url)
+    {
+        var index = this.index(url);
+        return index !== -1 ? this._items[index].scrollLineNumber : undefined;
+    },
+
+    /**
+     * @param {string} url
+     * @param {number} scrollLineNumber
+     */
+    updateScrollLineNumber: function(url, scrollLineNumber)
+    {
+        var index = this.index(url);
+        if (index === -1)
+            return;
+        this._items[index].scrollLineNumber = scrollLineNumber;
+    },
+
+    /**
+     * @param {Array.<string>} urls
+     */
+    update: function(urls)
+    {
+        for (var i = urls.length - 1; i >= 0; --i) {
+            var index = this.index(urls[i]);
+            var item;
+            if (index !== -1) {
+                item = this._items[index];
+                this._items.splice(index, 1);
+            } else
+                item = new WebInspector.TabbedEditorContainer.HistoryItem(urls[i]);
+            this._items.unshift(item);
+            this._rebuildItemIndex();
+        }
+    },
+
+    /**
+     * @param {string} url
+     */
+    remove: function(url)
+    {
+        var index = this.index(url);
+        if (index !== -1) {
+            this._items.splice(index, 1);
+            this._rebuildItemIndex();
+        }
+    },
+    
+    /**
+     * @param {WebInspector.Setting} setting
+     */
+    save: function(setting)
+    {
+        setting.set(this._serializeToObject());
+    },
+    
+    /**
+     * @return {!Array.<!Object>}
+     */
+    _serializeToObject: function()
+    {
+        var serializedHistory = [];
+        for (var i = 0; i < this._items.length; ++i) {
+            var serializedItem = this._items[i].serializeToObject();
+            if (serializedItem)
+                serializedHistory.push(serializedItem);
+            if (serializedHistory.length === WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount)
+                break;
+        }
+        return serializedHistory;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TabbedPaneTabDelegate}
+ * @param {WebInspector.TabbedEditorContainer} editorContainer
+ */
+WebInspector.EditorContainerTabDelegate = function(editorContainer)
+{
+    this._editorContainer = editorContainer;
+}
+
+WebInspector.EditorContainerTabDelegate.prototype = {
+    /**
+     * @param {WebInspector.TabbedPane} tabbedPane
+     * @param {Array.<string>} ids
+     */
+    closeTabs: function(tabbedPane, ids)
+    {
+        this._editorContainer._closeTabs(ids);
+    }
+}
diff --git a/Source/devtools/front_end/TabbedPane.js b/Source/devtools/front_end/TabbedPane.js
new file mode 100644
index 0000000..125bd5a
--- /dev/null
+++ b/Source/devtools/front_end/TabbedPane.js
@@ -0,0 +1,866 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @extends {WebInspector.View}
+ * @constructor
+ */
+WebInspector.TabbedPane = function()
+{
+    WebInspector.View.call(this);
+    this.registerRequiredCSS("tabbedPane.css");
+    this.element.addStyleClass("tabbed-pane");
+    this._headerElement = this.element.createChild("div", "tabbed-pane-header");
+    this._headerContentsElement = this._headerElement.createChild("div", "tabbed-pane-header-contents");
+    this._tabsElement = this._headerContentsElement.createChild("div", "tabbed-pane-header-tabs");
+    this._contentElement = this.element.createChild("div", "tabbed-pane-content scroll-target");
+    this._tabs = [];
+    this._tabsHistory = [];
+    this._tabsById = {};
+    this.element.addEventListener("click", this.focus.bind(this), true);
+    this.element.addEventListener("mouseup", this.onMouseUp.bind(this), false);
+
+    this._dropDownButton = this._createDropDownButton();
+}
+
+WebInspector.TabbedPane.EventTypes = {
+    TabSelected: "TabSelected",
+    TabClosed: "TabClosed"
+}
+
+WebInspector.TabbedPane.prototype = {
+    /**
+     * @return {WebInspector.View}
+     */
+    get visibleView()
+    {
+        return this._currentTab ? this._currentTab.view : null;
+    },
+
+    /**
+     * @return {string}
+     */
+    get selectedTabId()
+    {
+        return this._currentTab ? this._currentTab.id : null;
+    },
+
+    /**
+     * @type {boolean} shrinkableTabs
+     */
+    set shrinkableTabs(shrinkableTabs)
+    {
+        this._shrinkableTabs = shrinkableTabs;
+    },
+
+    /**
+     * @type {boolean} verticalTabLayout
+     */
+    set verticalTabLayout(verticalTabLayout)
+    {
+        this._verticalTabLayout = verticalTabLayout;
+    },
+
+    /**
+     * @type {boolean} shrinkableTabs
+     */
+    set closeableTabs(closeableTabs)
+    {
+        this._closeableTabs = closeableTabs;
+    },
+
+    defaultFocusedElement: function()
+    {
+        return this.visibleView ? this.visibleView.defaultFocusedElement() : null;
+    },
+
+    /**
+     * @param {WebInspector.TabbedPaneTabDelegate} delegate
+     */
+    setTabDelegate: function(delegate)
+    {
+        var tabs = this._tabs.slice();
+        for (var i = 0; i < tabs.length; ++i)
+            tabs[i].setDelegate(delegate);
+        this._delegate = delegate;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    onMouseUp: function(event)
+    {
+        // This is needed to prevent middle-click pasting on linux when tabs are clicked.
+        if (event.button === 1)
+            event.consume(true);
+    },
+
+    /**
+     * @param {string} id
+     * @param {string} tabTitle
+     * @param {WebInspector.View} view
+     * @param {string=} tabTooltip
+     * @param {boolean=} userGesture
+     */
+    appendTab: function(id, tabTitle, view, tabTooltip, userGesture)
+    {
+        var tab = new WebInspector.TabbedPaneTab(this, id, tabTitle, this._closeableTabs, view, tabTooltip);
+        tab.setDelegate(this._delegate);
+        this._tabsById[id] = tab;
+
+        this._tabs.push(tab);
+        this._tabsHistory.push(tab);
+
+        if (this._tabsHistory[0] === tab)
+            this.selectTab(tab.id, userGesture);
+
+        this._updateTabElements();
+    },
+
+    /**
+     * @param {string} id
+     * @param {boolean=} userGesture
+     */
+    closeTab: function(id, userGesture)
+    {
+        this.closeTabs([id], userGesture);
+    },
+
+     /**
+      * @param {Array.<string>} ids
+      * @param {boolean=} userGesture
+      */
+     closeTabs: function(ids, userGesture)
+     {
+         for (var i = 0; i < ids.length; ++i)
+             this._innerCloseTab(ids[i], userGesture);
+         this._updateTabElements();
+         if (this._tabsHistory.length)
+             this.selectTab(this._tabsHistory[0].id, userGesture);
+     },
+
+    /**
+     * @param {string} id
+     * @param {boolean=} userGesture
+     */
+    _innerCloseTab: function(id, userGesture)
+    {
+        if (this._currentTab && this._currentTab.id === id)
+            this._hideCurrentTab();
+
+        var tab = this._tabsById[id];
+        delete this._tabsById[id];
+
+        this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1);
+        this._tabs.splice(this._tabs.indexOf(tab), 1);
+        if (tab._shown)
+            this._hideTabElement(tab);
+
+        var eventData = { tabId: id, view: tab.view, isUserGesture: userGesture };
+        this.dispatchEventToListeners(WebInspector.TabbedPane.EventTypes.TabClosed, eventData);
+        return true;
+    },
+
+    /**
+     * @return {Array.<string>}
+     */
+    allTabs: function()
+    {
+        var result = [];
+        var tabs = this._tabs.slice();
+        for (var i = 0; i < tabs.length; ++i)
+            result.push(tabs[i].id);
+        return result;
+    },
+
+    /**
+     * @param {string} id
+     * @return {Array.<string>}
+     */
+    otherTabs: function(id)
+    {
+        var result = [];
+        var tabs = this._tabs.slice();
+        for (var i = 0; i < tabs.length; ++i) {
+            if (tabs[i].id !== id)
+                result.push(tabs[i].id);
+        }
+        return result;
+    },
+
+    /**
+     * @param {string} id
+     * @param {boolean=} userGesture
+     */
+    selectTab: function(id, userGesture)
+    {
+        var tab = this._tabsById[id];
+        if (!tab)
+            return;
+        if (this._currentTab && this._currentTab.id === id)
+            return;
+
+        this._hideCurrentTab();
+        this._showTab(tab);
+        this._currentTab = tab;
+        
+        this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1);
+        this._tabsHistory.splice(0, 0, tab);
+        
+        this._updateTabElements();
+
+        var eventData = { tabId: id, view: tab.view, isUserGesture: userGesture };
+        this.dispatchEventToListeners(WebInspector.TabbedPane.EventTypes.TabSelected, eventData);
+        return true;
+    },
+
+    /**
+     * @param {number} tabsCount
+     * @return {Array.<string>}
+     */
+    lastOpenedTabIds: function(tabsCount)
+    {
+        function tabToTabId(tab) {
+            return tab.id;
+        }
+
+        return this._tabsHistory.slice(0, tabsCount).map(tabToTabId);
+    },
+
+    /**
+     * @param {string} id
+     * @param {string} tabTitle
+     */
+    changeTabTitle: function(id, tabTitle)
+    {
+        var tab = this._tabsById[id];
+        tab.title = tabTitle;
+        this._updateTabElements();
+    },
+
+    /**
+     * @param {string} id
+     * @param {WebInspector.View} view
+     */
+    changeTabView: function(id, view)
+    {
+        var tab = this._tabsById[id];
+        if (this._currentTab && this._currentTab.id === tab.id) {
+            this._hideTab(tab);
+            tab.view = view;
+            this._showTab(tab);
+        } else
+            tab.view = view;
+    },
+
+    /**
+     * @param {string} id
+     * @param {string=} tabTooltip
+     */
+    changeTabTooltip: function(id, tabTooltip)
+    {
+        var tab = this._tabsById[id];
+        tab.tooltip = tabTooltip;
+    },
+
+    onResize: function()
+    {
+        this._updateTabElements();
+    },
+
+    _updateTabElements: function()
+    {
+        WebInspector.invokeOnceAfterBatchUpdate(this, this._innerUpdateTabElements);
+    },
+
+    _innerUpdateTabElements: function()
+    {
+        if (!this.isShowing())
+            return;
+
+        if (!this._tabs.length)
+            this._contentElement.addStyleClass("has-no-tabs");
+        else
+            this._contentElement.removeStyleClass("has-no-tabs");
+        
+        if (!this._measuredDropDownButtonWidth)
+            this._measureDropDownButton();
+
+        this._updateWidths();
+        this._updateTabsDropDown();
+    },
+
+    /**
+     * @param {number} index
+     * @param {WebInspector.TabbedPaneTab} tab
+     */
+    _showTabElement: function(index, tab)
+    {
+        if (index >= this._tabsElement.children.length)
+            this._tabsElement.appendChild(tab.tabElement);
+        else
+            this._tabsElement.insertBefore(tab.tabElement, this._tabsElement.children[index]);
+        tab._shown = true;
+    },
+
+    /**
+     * @param {WebInspector.TabbedPaneTab} tab
+     */
+    _hideTabElement: function(tab)
+    {
+        this._tabsElement.removeChild(tab.tabElement);
+        tab._shown = false;
+    },
+
+    _createDropDownButton: function()
+    {
+        var dropDownContainer = document.createElement("div");
+        dropDownContainer.addStyleClass("tabbed-pane-header-tabs-drop-down-container");
+        var dropDownButton = dropDownContainer.createChild("div", "tabbed-pane-header-tabs-drop-down");
+        dropDownButton.appendChild(document.createTextNode("\u00bb"));
+        this._tabsSelect = dropDownButton.createChild("select", "tabbed-pane-header-tabs-drop-down-select");
+        this._tabsSelect.addEventListener("change", this._tabsSelectChanged.bind(this), false);
+        return dropDownContainer;
+    },
+
+    _totalWidth: function()
+    {
+        return this._headerContentsElement.getBoundingClientRect().width;
+    },
+
+    _updateTabsDropDown: function()
+    {
+        var tabsToShowIndexes = this._tabsToShowIndexes(this._tabs, this._tabsHistory, this._totalWidth(), this._measuredDropDownButtonWidth);
+
+        for (var i = 0; i < this._tabs.length; ++i) {
+            if (this._tabs[i]._shown && tabsToShowIndexes.indexOf(i) === -1)
+                this._hideTabElement(this._tabs[i]);
+        }
+        for (var i = 0; i < tabsToShowIndexes.length; ++i) {
+            var tab = this._tabs[tabsToShowIndexes[i]];
+            if (!tab._shown)
+                this._showTabElement(i, tab);
+        }
+        
+        this._populateDropDownFromIndex();
+    },
+
+    _populateDropDownFromIndex: function()
+    {
+        if (this._dropDownButton.parentElement)
+            this._headerContentsElement.removeChild(this._dropDownButton);
+
+        this._tabsSelect.removeChildren();
+        var tabsToShow = [];
+        for (var i = 0; i < this._tabs.length; ++i) {
+            if (!this._tabs[i]._shown)
+                tabsToShow.push(this._tabs[i]);
+                continue;
+        }
+
+        function compareFunction(tab1, tab2)
+        {
+            return tab1.title.localeCompare(tab2.title);
+        }
+        tabsToShow.sort(compareFunction);
+
+        for (var i = 0; i < tabsToShow.length; ++i) {
+            var option = new Option(tabsToShow[i].title);
+            option.tab = tabsToShow[i];
+            this._tabsSelect.appendChild(option);
+        }
+        if (this._tabsSelect.options.length) {
+            this._headerContentsElement.appendChild(this._dropDownButton);
+            this._tabsSelect.selectedIndex = -1;
+        }
+    },
+
+    _tabsSelectChanged: function()
+    {
+        var options = this._tabsSelect.options;
+        var selectedOption = options[this._tabsSelect.selectedIndex];
+        this.selectTab(selectedOption.tab.id, true);
+    },
+
+    _measureDropDownButton: function()
+    {
+        this._dropDownButton.addStyleClass("measuring");
+        this._headerContentsElement.appendChild(this._dropDownButton);
+        this._measuredDropDownButtonWidth = this._dropDownButton.getBoundingClientRect().width;
+        this._headerContentsElement.removeChild(this._dropDownButton);
+        this._dropDownButton.removeStyleClass("measuring");
+    },
+
+    _updateWidths: function()
+    {
+        var measuredWidths = this._measureWidths();
+        var maxWidth = this._shrinkableTabs ? this._calculateMaxWidth(measuredWidths.slice(), this._totalWidth()) : Number.MAX_VALUE;
+
+        var i = 0;
+        for (var tabId in this._tabs) {
+            var tab = this._tabs[tabId];
+            tab.setWidth(this._verticalTabLayout ? -1 : Math.min(maxWidth, measuredWidths[i++]));
+        }
+    },
+
+    _measureWidths: function()
+    {
+        // Add all elements to measure into this._tabsElement
+        var measuringTabElements = [];
+        for (var tabId in this._tabs) {
+            var tab = this._tabs[tabId];
+            if (typeof tab._measuredWidth === "number")
+                continue;
+            var measuringTabElement = tab._createTabElement(true);
+            measuringTabElement.__tab = tab;
+            measuringTabElements.push(measuringTabElement);
+            this._tabsElement.appendChild(measuringTabElement);
+        }
+
+        // Perform measurement
+        for (var i = 0; i < measuringTabElements.length; ++i)
+            measuringTabElements[i].__tab._measuredWidth = measuringTabElements[i].getBoundingClientRect().width;
+
+        // Nuke elements from the UI
+        for (var i = 0; i < measuringTabElements.length; ++i)
+            measuringTabElements[i].parentElement.removeChild(measuringTabElements[i]);
+
+        // Combine the results.
+        var measuredWidths = [];
+        for (var tabId in this._tabs)
+            measuredWidths.push(this._tabs[tabId]._measuredWidth);
+
+        return measuredWidths;
+    },
+
+    /**
+     * @param {Array.<number>} measuredWidths
+     * @param {number} totalWidth
+     */
+    _calculateMaxWidth: function(measuredWidths, totalWidth)
+    {
+        if (!measuredWidths.length)
+            return 0;
+
+        measuredWidths.sort(function(x, y) { return x - y });
+
+        var totalMeasuredWidth = 0;
+        for (var i = 0; i < measuredWidths.length; ++i)
+            totalMeasuredWidth += measuredWidths[i];
+
+        if (totalWidth >= totalMeasuredWidth)
+            return measuredWidths[measuredWidths.length - 1];
+
+        var totalExtraWidth = 0;
+        for (var i = measuredWidths.length - 1; i > 0; --i) {
+            var extraWidth = measuredWidths[i] - measuredWidths[i - 1];
+            totalExtraWidth += (measuredWidths.length - i) * extraWidth;
+
+            if (totalWidth + totalExtraWidth >= totalMeasuredWidth)
+                return measuredWidths[i - 1] + (totalWidth + totalExtraWidth - totalMeasuredWidth) / (measuredWidths.length - i); 
+        }
+
+        return totalWidth / measuredWidths.length;
+    },
+
+    /**
+     * @param {Array.<WebInspector.TabbedPaneTab>} tabsOrdered
+     * @param {Array.<WebInspector.TabbedPaneTab>} tabsHistory
+     * @param {number} totalWidth
+     * @param {number} measuredDropDownButtonWidth
+     * @return {Array.<number>}
+     */
+    _tabsToShowIndexes: function(tabsOrdered, tabsHistory, totalWidth, measuredDropDownButtonWidth)
+    {
+        var tabsToShowIndexes = [];
+
+        var totalTabsWidth = 0;
+        for (var i = 0; i < tabsHistory.length; ++i) {
+            totalTabsWidth += tabsHistory[i].width();
+            var minimalRequiredWidth = totalTabsWidth;
+            if (i !== tabsHistory.length - 1)
+                minimalRequiredWidth += measuredDropDownButtonWidth;
+            if (!this._verticalTabLayout && minimalRequiredWidth > totalWidth)
+                break;
+            tabsToShowIndexes.push(tabsOrdered.indexOf(tabsHistory[i]));
+        }
+        
+        tabsToShowIndexes.sort(function(x, y) { return x - y });
+        
+        return tabsToShowIndexes;
+    },
+    
+    _hideCurrentTab: function()
+    {
+        if (!this._currentTab)
+            return;
+
+        this._hideTab(this._currentTab);
+        delete this._currentTab;
+    },
+
+    /**
+     * @param {WebInspector.TabbedPaneTab} tab
+     */
+    _showTab: function(tab)
+    {
+        tab.tabElement.addStyleClass("selected");
+        tab.view.show(this._contentElement);
+    },
+
+    /**
+     * @param {WebInspector.TabbedPaneTab} tab
+     */
+    _hideTab: function(tab)
+    {
+        tab.tabElement.removeStyleClass("selected");
+        tab.view.detach();
+    },
+
+    canHighlightLine: function()
+    {
+        return this._currentTab && this._currentTab.view && this._currentTab.view.canHighlightLine();
+    },
+
+    highlightLine: function(line)
+    {
+        if (this.canHighlightLine())
+            this._currentTab.view.highlightLine(line);
+    },
+
+    /**
+     * @return {Array.<Element>}
+     */
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return [ this._contentElement ];
+    },
+
+    /**
+     * @param {WebInspector.TabbedPaneTab} tab
+     * @param {number} index
+     */
+    _insertBefore: function(tab, index)
+    {
+        this._tabsElement.insertBefore(tab._tabElement, this._tabsElement.childNodes[index]);
+        var oldIndex = this._tabs.indexOf(tab);
+        this._tabs.splice(oldIndex, 1);
+        if (oldIndex < index)
+            --index;
+        this._tabs.splice(index, 0, tab);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+
+/**
+ * @constructor
+ * @param {WebInspector.TabbedPane} tabbedPane
+ * @param {string} id
+ * @param {string} title
+ * @param {boolean} closeable
+ * @param {WebInspector.View} view
+ * @param {string=} tooltip
+ */
+WebInspector.TabbedPaneTab = function(tabbedPane, id, title, closeable, view, tooltip)
+{
+    this._closeable = closeable;
+    this._tabbedPane = tabbedPane;
+    this._id = id;
+    this._title = title;
+    this._tooltip = tooltip;
+    this._view = view;
+    this._shown = false;
+    /** @type {number} */ this._measuredWidth;
+    /** @type {Element} */ this._tabElement;
+}
+
+WebInspector.TabbedPaneTab.prototype = {
+    /**
+     * @return {string}
+     */
+    get id()
+    {
+        return this._id;
+    },
+
+    /**
+     * @return {string}
+     */
+    get title()
+    {
+        return this._title;
+    },
+
+    set title(title)
+    {
+        if (title === this._title)
+            return;
+        this._title = title;
+        if (this._titleElement)
+            this._titleElement.textContent = title;
+        delete this._measuredWidth;
+    },
+
+    /**
+     * @return {WebInspector.View}
+     */
+    get view()
+    {
+        return this._view;
+    },
+
+    set view(view)
+    {
+        this._view = view;
+    },
+
+    /**
+     * @return {string|undefined}
+     */
+    get tooltip()
+    {
+        return this._tooltip;
+    },
+
+    set tooltip(tooltip)
+    {
+        this._tooltip = tooltip;
+        if (this._titleElement)
+            this._titleElement.title = tooltip || "";
+    },
+
+    /**
+     * @return {Element}
+     */
+    get tabElement()
+    {
+        if (typeof(this._tabElement) !== "undefined")
+            return this._tabElement;
+        
+        this._createTabElement(false);
+        return this._tabElement;
+    },
+
+    /**
+     * @return {number}
+     */
+    width: function()
+    {
+        return this._width;
+    },
+
+    /**
+     * @param {number} width
+     */
+    setWidth: function(width)
+    {
+        this.tabElement.style.width = width === -1 ? "" : (width + "px");
+        this._width = width;
+    },
+
+    /**
+     * @param {WebInspector.TabbedPaneTabDelegate} delegate
+     */
+    setDelegate: function(delegate)
+    {
+        this._delegate = delegate;
+    },
+
+    /**
+     * @param {boolean} measuring
+     */
+    _createTabElement: function(measuring)
+    {
+        var tabElement = document.createElement("div");
+        tabElement.addStyleClass("tabbed-pane-header-tab");
+        tabElement.id = "tab-" + this._id;
+        tabElement.tabIndex = -1;
+
+        var titleElement = tabElement.createChild("span", "tabbed-pane-header-tab-title");
+        titleElement.textContent = this.title;
+        titleElement.title = this.tooltip || "";
+        if (!measuring)
+            this._titleElement = titleElement;
+
+        if (this._closeable)
+            tabElement.createChild("div", "close-button-gray");
+
+        if (measuring)
+            tabElement.addStyleClass("measuring");
+        else {
+            this._tabElement = tabElement;
+            tabElement.addEventListener("click", this._tabClicked.bind(this), false);
+            tabElement.addEventListener("mousedown", this._tabMouseDown.bind(this), false);
+            if (this._closeable) {
+                tabElement.addEventListener("contextmenu", this._tabContextMenu.bind(this), false);
+                WebInspector.installDragHandle(tabElement, this._startTabDragging.bind(this), this._tabDragging.bind(this), this._endTabDragging.bind(this), "pointer");
+            }
+        }
+
+        return tabElement;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _tabClicked: function(event)
+    {
+        if (this._closeable && (event.button === 1 || event.target.hasStyleClass("close-button-gray")))
+            this._closeTabs([this.id]);
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _tabMouseDown: function(event)
+    {
+        if (event.target.hasStyleClass("close-button-gray") || event.button === 1)
+            return;
+        this._tabbedPane.selectTab(this.id, true);
+    },
+
+    /**
+     * @param {Array.<string>} ids
+     */
+    _closeTabs: function(ids)
+    {
+        if (this._delegate) {
+            this._delegate.closeTabs(this._tabbedPane, ids);
+            return;
+        }
+        this._tabbedPane.closeTabs(ids, true);
+    },
+
+    _tabContextMenu: function(event)
+    {
+        function close()
+        {
+            this._closeTabs([this.id]);
+        }
+  
+        function closeOthers()
+        {
+            this._closeTabs(this._tabbedPane.otherTabs(this.id));
+        }
+  
+        function closeAll()
+        {
+            this._closeTabs(this._tabbedPane.allTabs(this.id));
+        }
+  
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString("Close"), close.bind(this));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Close others" : "Close Others"), closeOthers.bind(this));
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Close all" : "Close All"), closeAll.bind(this));
+        contextMenu.show();
+    },
+
+    /**
+     * @param {Event} event
+     * @return {boolean}
+     */
+    _startTabDragging: function(event)
+    {
+        if (event.target.hasStyleClass("close-button-gray"))
+            return false;
+        this._dragStartX = event.pageX;
+        return true;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _tabDragging: function(event)
+    {
+        var tabElements = this._tabbedPane._tabsElement.childNodes;
+        for (var i = 0; i < tabElements.length; ++i) {
+            var tabElement = tabElements[i];
+            if (tabElement === this._tabElement)
+                continue;
+
+            var intersects = tabElement.offsetLeft + tabElement.clientWidth > this._tabElement.offsetLeft &&
+                this._tabElement.offsetLeft + this._tabElement.clientWidth > tabElement.offsetLeft;
+            if (!intersects)
+                continue;
+
+            if (Math.abs(event.pageX - this._dragStartX) < tabElement.clientWidth / 2 + 5)
+                break;
+
+            if (event.pageX - this._dragStartX > 0) {
+                tabElement = tabElement.nextSibling;
+                ++i;
+            }
+
+            var oldOffsetLeft = this._tabElement.offsetLeft;
+            this._tabbedPane._insertBefore(this, i);
+            this._dragStartX += this._tabElement.offsetLeft - oldOffsetLeft;
+            break;
+        }
+
+        if (!this._tabElement.previousSibling && event.pageX - this._dragStartX < 0) {
+            this._tabElement.style.setProperty("left", "0px");
+            return;
+        }
+        if (!this._tabElement.nextSibling && event.pageX - this._dragStartX > 0) {
+            this._tabElement.style.setProperty("left", "0px");
+            return;
+        }
+
+        this._tabElement.style.setProperty("position", "relative");
+        this._tabElement.style.setProperty("left", (event.pageX - this._dragStartX) + "px");
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _endTabDragging: function(event)
+    {
+        this._tabElement.style.removeProperty("position");
+        this._tabElement.style.removeProperty("left");
+        delete this._dragStartX;
+    }
+}
+
+/**
+ * @interface
+ */
+WebInspector.TabbedPaneTabDelegate = function()
+{
+}
+
+WebInspector.TabbedPaneTabDelegate.prototype = {
+    /**
+     * @param {WebInspector.TabbedPane} tabbedPane
+     * @param {Array.<string>} ids
+     */
+    closeTabs: function(tabbedPane, ids) { }
+}
diff --git a/Source/devtools/front_end/TestController.js b/Source/devtools/front_end/TestController.js
new file mode 100644
index 0000000..cf0c32e
--- /dev/null
+++ b/Source/devtools/front_end/TestController.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.evaluateForTestInFrontend = function(callId, script)
+{
+    if (!InspectorFrontendHost.isUnderTest())
+        return;
+
+    function invokeMethod()
+    {
+        var message;
+        try {
+            script = script + "//@ sourceURL=evaluateInWebInspector" + callId + ".js";
+            var result = window.eval(script);
+            message = typeof result === "undefined" ? "\"<undefined>\"" : JSON.stringify(result);
+        } catch (e) {
+            message = e.toString();
+        }
+        RuntimeAgent.evaluate("didEvaluateForTestInFrontend(" + callId + ", " + message + ")", "test");
+    }
+    InspectorBackend.runAfterPendingDispatches(invokeMethod);
+}
diff --git a/Source/devtools/front_end/Tests.js b/Source/devtools/front_end/Tests.js
new file mode 100644
index 0000000..d541b02
--- /dev/null
+++ b/Source/devtools/front_end/Tests.js
@@ -0,0 +1,1003 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+
+/**
+ * @fileoverview This file contains small testing framework along with the
+ * test suite for the frontend. These tests are a part of the continues build
+ * and are executed by the devtools_sanity_unittest.cc as a part of the
+ * Interactive UI Test suite.
+ * FIXME: change field naming style to use trailing underscore.
+ */
+
+if (window.domAutomationController) {
+
+var ___interactiveUiTestsMode = true;
+
+/**
+ * Test suite for interactive UI tests.
+ * @constructor
+ */
+TestSuite = function()
+{
+    this.controlTaken_ = false;
+    this.timerId_ = -1;
+};
+
+
+/**
+ * Reports test failure.
+ * @param {string} message Failure description.
+ */
+TestSuite.prototype.fail = function(message)
+{
+    if (this.controlTaken_)
+        this.reportFailure_(message);
+    else
+        throw message;
+};
+
+
+/**
+ * Equals assertion tests that expected === actual.
+ * @param {Object} expected Expected object.
+ * @param {Object} actual Actual object.
+ * @param {string} opt_message User message to print if the test fails.
+ */
+TestSuite.prototype.assertEquals = function(expected, actual, opt_message)
+{
+    if (expected !== actual) {
+        var message = "Expected: '" + expected + "', but was '" + actual + "'";
+        if (opt_message)
+            message = opt_message + "(" + message + ")";
+        this.fail(message);
+    }
+};
+
+/**
+ * True assertion tests that value == true.
+ * @param {Object} value Actual object.
+ * @param {string} opt_message User message to print if the test fails.
+ */
+TestSuite.prototype.assertTrue = function(value, opt_message)
+{
+    this.assertEquals(true, !!value, opt_message);
+};
+
+
+/**
+ * HasKey assertion tests that object has given key.
+ * @param {Object} object
+ * @param {string} key
+ */
+TestSuite.prototype.assertHasKey = function(object, key)
+{
+    if (!object.hasOwnProperty(key))
+        this.fail("Expected object to contain key '" + key + "'");
+};
+
+
+/**
+ * Contains assertion tests that string contains substring.
+ * @param {string} string Outer.
+ * @param {string} substring Inner.
+ */
+TestSuite.prototype.assertContains = function(string, substring)
+{
+    if (string.indexOf(substring) === -1)
+        this.fail("Expected to: '" + string + "' to contain '" + substring + "'");
+};
+
+
+/**
+ * Takes control over execution.
+ */
+TestSuite.prototype.takeControl = function()
+{
+    this.controlTaken_ = true;
+    // Set up guard timer.
+    var self = this;
+    this.timerId_ = setTimeout(function() {
+        self.reportFailure_("Timeout exceeded: 20 sec");
+    }, 20000);
+};
+
+
+/**
+ * Releases control over execution.
+ */
+TestSuite.prototype.releaseControl = function()
+{
+    if (this.timerId_ !== -1) {
+        clearTimeout(this.timerId_);
+        this.timerId_ = -1;
+    }
+    this.reportOk_();
+};
+
+
+/**
+ * Async tests use this one to report that they are completed.
+ */
+TestSuite.prototype.reportOk_ = function()
+{
+    window.domAutomationController.send("[OK]");
+};
+
+
+/**
+ * Async tests use this one to report failures.
+ */
+TestSuite.prototype.reportFailure_ = function(error)
+{
+    if (this.timerId_ !== -1) {
+        clearTimeout(this.timerId_);
+        this.timerId_ = -1;
+    }
+    window.domAutomationController.send("[FAILED] " + error);
+};
+
+
+/**
+ * Runs all global functions starting with "test" as unit tests.
+ */
+TestSuite.prototype.runTest = function(testName)
+{
+    try {
+        this[testName]();
+        if (!this.controlTaken_)
+            this.reportOk_();
+    } catch (e) {
+        this.reportFailure_(e);
+    }
+};
+
+
+/**
+ * @param {string} panelName Name of the panel to show.
+ */
+TestSuite.prototype.showPanel = function(panelName)
+{
+    // Open Scripts panel.
+    var toolbar = document.getElementById("toolbar");
+    var button = toolbar.getElementsByClassName(panelName)[0];
+    button.click();
+    this.assertEquals(WebInspector.panels[panelName], WebInspector.inspectorView.currentPanel());
+};
+
+
+/**
+ * Overrides the method with specified name until it's called first time.
+ * @param {Object} receiver An object whose method to override.
+ * @param {string} methodName Name of the method to override.
+ * @param {Function} override A function that should be called right after the
+ *     overriden method returns.
+ * @param {boolean} opt_sticky Whether restore original method after first run
+ *     or not.
+ */
+TestSuite.prototype.addSniffer = function(receiver, methodName, override, opt_sticky)
+{
+    var orig = receiver[methodName];
+    if (typeof orig !== "function")
+        this.fail("Cannot find method to override: " + methodName);
+    var test = this;
+    receiver[methodName] = function(var_args) {
+        try {
+            var result = orig.apply(this, arguments);
+        } finally {
+            if (!opt_sticky)
+                receiver[methodName] = orig;
+        }
+        // In case of exception the override won't be called.
+        try {
+            override.apply(this, arguments);
+        } catch (e) {
+            test.fail("Exception in overriden method '" + methodName + "': " + e);
+        }
+        return result;
+    };
+};
+
+
+TestSuite.prototype.testEnableResourcesTab = function()
+{
+    // FIXME once reference is removed downstream.
+}
+
+TestSuite.prototype.testCompletionOnPause = function()
+{
+    // FIXME once reference is removed downstream.
+}
+
+// UI Tests
+
+
+/**
+ * Tests that scripts tab can be open and populated with inspected scripts.
+ */
+TestSuite.prototype.testShowScriptsTab = function()
+{
+    this.showPanel("scripts");
+    var test = this;
+    // There should be at least main page script.
+    this._waitUntilScriptsAreParsed(["debugger_test_page.html"],
+        function() {
+            test.releaseControl();
+        });
+    // Wait until all scripts are added to the debugger.
+    this.takeControl();
+};
+
+
+/**
+ * Tests that scripts tab is populated with inspected scripts even if it
+ * hadn't been shown by the moment inspected paged refreshed.
+ * @see http://crbug.com/26312
+ */
+TestSuite.prototype.testScriptsTabIsPopulatedOnInspectedPageRefresh = function()
+{
+    var test = this;
+    this.assertEquals(WebInspector.panels.elements, WebInspector.inspectorView.currentPanel(), "Elements panel should be current one.");
+
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, waitUntilScriptIsParsed);
+
+    // Reload inspected page. It will reset the debugger agent.
+    test.evaluateInConsole_("window.location.reload(true);", function(resultText) {});
+
+    function waitUntilScriptIsParsed()
+    {
+        WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, waitUntilScriptIsParsed);
+        test.showPanel("scripts");
+        test._waitUntilScriptsAreParsed(["debugger_test_page.html"],
+            function() {
+                test.releaseControl();
+            });
+    }
+
+    // Wait until all scripts are added to the debugger.
+    this.takeControl();
+};
+
+
+/**
+ * Tests that scripts list contains content scripts.
+ */
+TestSuite.prototype.testContentScriptIsPresent = function()
+{
+    this.showPanel("scripts");
+    var test = this;
+
+    test._waitUntilScriptsAreParsed(
+        ["page_with_content_script.html", "simple_content_script.js"],
+        function() {
+          test.releaseControl();
+        });
+
+    // Wait until all scripts are added to the debugger.
+    this.takeControl();
+};
+
+
+/**
+ * Tests renderer process memory size obtained and passed to inspector
+ * successfully.
+ */
+TestSuite.prototype.testRendererProcessNativeMemorySize = function()
+{
+    var test = this;
+    var KB = 1024;
+    var MB = KB * KB;
+
+    function step1(error, memoryBlock)
+    {
+        test.assertTrue(!error, "An error has occurred: " + error);
+        test.assertTrue(memoryBlock.size > 1 * MB && memoryBlock.size < 1500 * MB, "Unfeasible process size: " + memoryBlock.size + " bytes.");
+
+        test.releaseControl();
+    }
+
+    MemoryAgent.getProcessMemoryDistribution(false, step1);
+
+    this.takeControl();
+};
+
+
+/**
+ * Tests that scripts are not duplicaed on Scripts tab switch.
+ */
+TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function()
+{
+    var test = this;
+
+    // There should be two scripts: one for the main page and another
+    // one which is source of console API(see
+    // InjectedScript._ensureCommandLineAPIInstalled).
+    var expectedScriptsCount = 2;
+    var parsedScripts = [];
+
+    this.showPanel("scripts");
+
+    function switchToElementsTab() {
+        test.showPanel("elements");
+        setTimeout(switchToScriptsTab, 0);
+    }
+
+    function switchToScriptsTab() {
+        test.showPanel("scripts");
+        setTimeout(checkScriptsPanel, 0);
+    }
+
+    function checkScriptsPanel() {
+        test.assertTrue(test._scriptsAreParsed(["debugger_test_page.html"]), "Some scripts are missing.");
+        checkNoDuplicates();
+        test.releaseControl();
+    }
+
+    function checkNoDuplicates() {
+        var uiSourceCodes = test.nonAnonymousUISourceCodes_();
+        for (var i = 0; i < uiSourceCodes.length; i++) {
+            var scriptName = uiSourceCodes[i].url;
+            for (var j = i + 1; j < uiSourceCodes.length; j++)
+                test.assertTrue(scriptName !== uiSourceCodes[j].url, "Found script duplicates: " + test.uiSourceCodesToString_(uiSourceCodes));
+        }
+    }
+
+    test._waitUntilScriptsAreParsed(
+        ["debugger_test_page.html"],
+        function() {
+            checkNoDuplicates();
+            setTimeout(switchToElementsTab, 0);
+        });
+
+
+    // Wait until all scripts are added to the debugger.
+    this.takeControl();
+};
+
+
+// Tests that debugger works correctly if pause event occurs when DevTools
+// frontend is being loaded.
+TestSuite.prototype.testPauseWhenLoadingDevTools = function()
+{
+    this.showPanel("scripts");
+
+    // Script execution can already be paused.
+    if (WebInspector.debuggerModel.debuggerPausedDetails)
+        return;
+
+    this._waitForScriptPause(this.releaseControl.bind(this));
+    this.takeControl();
+};
+
+
+// Tests that pressing "Pause" will pause script execution if the script
+// is already running.
+TestSuite.prototype.testPauseWhenScriptIsRunning = function()
+{
+    this.showPanel("scripts");
+
+    this.evaluateInConsole_(
+        'setTimeout("handleClick()" , 0)',
+        didEvaluateInConsole.bind(this));
+
+    function didEvaluateInConsole(resultText) {
+        this.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText);
+        // Wait for some time to make sure that inspected page is running the
+        // infinite loop.
+        setTimeout(testScriptPause.bind(this), 300);
+    }
+
+    function testScriptPause() {
+        // The script should be in infinite loop. Click "Pause" button to
+        // pause it and wait for the result.
+        WebInspector.panels.scripts._pauseButton.element.click();
+
+        this._waitForScriptPause(this.releaseControl.bind(this));
+    }
+
+    this.takeControl();
+};
+
+
+/**
+ * Tests network size.
+ */
+TestSuite.prototype.testNetworkSize = function()
+{
+    var test = this;
+
+    function finishResource(resource, finishTime)
+    {
+        test.assertEquals(219, resource.transferSize, "Incorrect total encoded data length");
+        test.assertEquals(25, resource.resourceSize, "Incorrect total data length");
+        test.releaseControl();
+    }
+
+    this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource);
+
+    // Reload inspected page to sniff network events
+    test.evaluateInConsole_("window.location.reload(true);", function(resultText) {});
+
+    this.takeControl();
+};
+
+
+/**
+ * Tests network sync size.
+ */
+TestSuite.prototype.testNetworkSyncSize = function()
+{
+    var test = this;
+
+    function finishResource(resource, finishTime)
+    {
+        test.assertEquals(219, resource.transferSize, "Incorrect total encoded data length");
+        test.assertEquals(25, resource.resourceSize, "Incorrect total data length");
+        test.releaseControl();
+    }
+
+    this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource);
+
+    // Send synchronous XHR to sniff network events
+    test.evaluateInConsole_("var xhr = new XMLHttpRequest(); xhr.open(\"GET\", \"chunked\", false); xhr.send(null);", function() {});
+
+    this.takeControl();
+};
+
+
+/**
+ * Tests network raw headers text.
+ */
+TestSuite.prototype.testNetworkRawHeadersText = function()
+{
+    var test = this;
+
+    function finishResource(resource, finishTime)
+    {
+        if (!resource.responseHeadersText)
+            test.fail("Failure: resource does not have response headers text");
+        test.assertEquals(164, resource.responseHeadersText.length, "Incorrect response headers text length");
+        test.releaseControl();
+    }
+
+    this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource);
+
+    // Reload inspected page to sniff network events
+    test.evaluateInConsole_("window.location.reload(true);", function(resultText) {});
+
+    this.takeControl();
+};
+
+
+/**
+ * Tests network timing.
+ */
+TestSuite.prototype.testNetworkTiming = function()
+{
+    var test = this;
+
+    function finishResource(resource, finishTime)
+    {
+        // Setting relaxed expectations to reduce flakiness.
+        // Server sends headers after 100ms, then sends data during another 100ms.
+        // We expect these times to be measured at least as 70ms.
+        test.assertTrue(resource.timing.receiveHeadersEnd - resource.timing.connectStart >= 70,
+                        "Time between receiveHeadersEnd and connectStart should be >=70ms, but was " +
+                        "receiveHeadersEnd=" + resource.timing.receiveHeadersEnd + ", connectStart=" + resource.timing.connectStart + ".");
+        test.assertTrue(resource.responseReceivedTime - resource.startTime >= 0.07,
+                "Time between responseReceivedTime and startTime should be >=0.07s, but was " +
+                "responseReceivedTime=" + resource.responseReceivedTime + ", startTime=" + resource.startTime + ".");
+        test.assertTrue(resource.endTime - resource.startTime >= 0.14,
+                "Time between endTime and startTime should be >=0.14s, but was " +
+                "endtime=" + resource.endTime + ", startTime=" + resource.startTime + ".");
+
+        test.releaseControl();
+    }
+
+    this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource);
+
+    // Reload inspected page to sniff network events
+    test.evaluateInConsole_("window.location.reload(true);", function(resultText) {});
+
+    this.takeControl();
+};
+
+
+TestSuite.prototype.testConsoleOnNavigateBack = function()
+{
+    if (WebInspector.console.messages.length === 1)
+        firstConsoleMessageReceived.call(this);
+    else
+        WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, firstConsoleMessageReceived, this);
+
+    function firstConsoleMessageReceived() {
+        this.evaluateInConsole_("clickLink();", didClickLink.bind(this));
+    }
+
+    function didClickLink() {
+        // Check that there are no new messages(command is not a message).
+        this.assertEquals(1, WebInspector.console.messages.length);
+        this.assertEquals(1, WebInspector.console.messages[0].totalRepeatCount);
+        this.evaluateInConsole_("history.back();", didNavigateBack.bind(this));
+    }
+
+    function didNavigateBack()
+    {
+        // Make sure navigation completed and possible console messages were pushed.
+        this.evaluateInConsole_("void 0;", didCompleteNavigation.bind(this));
+    }
+
+    function didCompleteNavigation() {
+        this.assertEquals(1, WebInspector.console.messages.length);
+        this.assertEquals(1, WebInspector.console.messages[0].totalRepeatCount);
+        this.releaseControl();
+    }
+
+    this.takeControl();
+};
+
+
+TestSuite.prototype.testReattachAfterCrash = function()
+{
+    this.evaluateInConsole_("1+1;", this.releaseControl.bind(this));
+    this.takeControl();
+};
+
+
+TestSuite.prototype.testSharedWorker = function()
+{
+    function didEvaluateInConsole(resultText) {
+        this.assertEquals("2011", resultText);
+        this.releaseControl();
+    }
+    this.evaluateInConsole_("globalVar", didEvaluateInConsole.bind(this));
+    this.takeControl();
+};
+
+
+TestSuite.prototype.testPauseInSharedWorkerInitialization = function()
+{
+    if (WebInspector.debuggerModel.debuggerPausedDetails)
+        return;
+    this._waitForScriptPause(this.releaseControl.bind(this));
+    this.takeControl();
+};
+
+/**
+ * Tests that timeline receives frame signals.
+ */
+TestSuite.prototype.testTimelineFrames = function()
+{
+    var test = this;
+
+    function step1()
+    {
+        test.recordTimeline(onTimelineRecorded);
+        test.evaluateInConsole_("runTest()", function(){});
+    }
+
+    function onTimelineRecorded(records)
+    {
+        var frameCount = 0;
+        var recordsInFrame = {};
+
+        for (var i = 0; i < records.length; ++i) {
+            var record = records[i];
+            if (record.type !== "BeginFrame") {
+                recordsInFrame[record.type] = (recordsInFrame[record.type] || 0) + 1;
+                continue;
+            }
+            if (!frameCount++)
+                continue;
+
+            test.assertHasKey(recordsInFrame, "FireAnimationFrame");
+            test.assertHasKey(recordsInFrame, "Layout");
+            test.assertHasKey(recordsInFrame, "RecalculateStyles");
+            test.assertHasKey(recordsInFrame, "Paint");
+            recordsInFrame = {};
+        }
+        test.assertTrue(frameCount >= 5, "Not enough frames");
+        test.releaseControl();
+    }
+
+    step1();
+    test.takeControl();
+}
+
+// Regression test for http://webk.it/97466
+TestSuite.prototype.testPageOverlayUpdate = function()
+{
+    var test = this;
+
+    function populatePage()
+    {
+        var div1 = document.createElement("div");
+        div1.id = "div1";
+        // Force accelerated compositing.
+        div1.style.webkitTransform = "translateZ(0)";
+        document.body.appendChild(div1);
+        var div2 = document.createElement("div");
+        div2.id = "div2";
+        document.body.appendChild(div2);
+    }
+
+    function step1()
+    {
+        test.evaluateInConsole_(populatePage.toString() + "; populatePage();" +
+                                "inspect(document.getElementById('div1'))", function() {});
+        WebInspector.notifications.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, step2);
+    }
+
+    function step2()
+    {
+        WebInspector.notifications.removeEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, step2);
+        test.recordTimeline(onTimelineRecorded);
+        setTimeout(step3, 500);
+    }
+
+    function step3()
+    {
+        test.evaluateInConsole_("inspect(document.getElementById('div2'))", function() {});
+        WebInspector.notifications.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, step4);
+    }
+
+    function step4()
+    {
+        WebInspector.notifications.removeEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, step4);
+        test.stopTimeline();
+    }
+
+    function onTimelineRecorded(records)
+    {
+        var types = {};
+        for (var i = 0; i < records.length; ++i)
+            types[records[i].type] = (types[records[i].type] || 0) + 1;
+
+        var frameCount = types["BeginFrame"];
+        // There should be at least two updates caused by selection of nodes.
+        test.assertTrue(frameCount >= 2, "Not enough DevTools overlay updates");
+        // We normally expect up to 3 frames, but allow for a bit more in case
+        // of some unexpected invalidations.
+        test.assertTrue(frameCount < 6, "Too many updates caused by DevTools overlay");
+        test.releaseControl();
+    }
+
+    step1();
+    this.takeControl();
+}
+
+
+/**
+ * Records timeline till console.timeStamp("ready"), invokes callback with resulting records.
+ * @param {function(Array.<Object>)} callback
+ */
+TestSuite.prototype.recordTimeline = function(callback)
+{
+    var records = [];
+    var dispatchOnRecordType = {}
+
+    WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded, addRecord);
+    WebInspector.timelineManager.start();
+
+    function addRecord(event)
+    {
+        innerAddRecord(event.data);
+    }
+
+    function innerAddRecord(record)
+    {
+        records.push(record);
+        if (record.type === "TimeStamp" && record.data.message === "ready")
+            done();
+
+        if (record.children)
+            record.children.forEach(innerAddRecord);
+    }
+
+    function done()
+    {
+        WebInspector.timelineManager.stop();
+        WebInspector.timelineManager.removeEventListener(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded, addRecord);
+        callback(records);
+    }
+}
+
+
+TestSuite.prototype.stopTimeline = function()
+{
+    this.evaluateInConsole_("console.timeStamp('ready')", function() {});
+}
+
+TestSuite.prototype.waitForTestResultsInConsole = function()
+{
+    var messages = WebInspector.console.messages;
+    for (var i = 0; i < messages.length; ++i) {
+        var text = messages[i].text;
+        if (text === "PASS")
+            return;
+        else if (/^FAIL/.test(text))
+            this.fail(text); // This will throw.
+    }
+    // Neitwer PASS nor FAIL, so wait for more messages.
+    function onConsoleMessage(event)
+    {
+        var text = event.data.text;
+        if (text === "PASS")
+            this.releaseControl();
+        else if (/^FAIL/.test(text))
+            this.fail(text);
+    }
+
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
+    this.takeControl();
+};
+
+TestSuite.prototype.checkLogAndErrorMessages = function()
+{
+    var messages = WebInspector.console.messages;
+
+    var matchesCount = 0;
+    function validMessage(message)
+    {
+        if (message.text === "log" && message.level === WebInspector.ConsoleMessage.MessageLevel.Log) {
+            ++matchesCount;
+            return true;
+        }
+
+        if (message.text === "error" && message.level === WebInspector.ConsoleMessage.MessageLevel.Error) {
+            ++matchesCount;
+            return true;
+        }
+        return false;
+    }
+
+    for (var i = 0; i < messages.length; ++i) {
+        if (validMessage(messages[i]))
+            continue;
+        this.fail(messages[i].text + ":" + messages[i].level); // This will throw.
+    }
+
+    if (matchesCount === 2)
+        return;
+
+    // Wait for more messages.
+    function onConsoleMessage(event)
+    {
+        var message = event.data;
+        if (validMessage(message)) {
+            if (matchesCount === 2) {
+                this.releaseControl();
+                return;
+            }
+        } else
+            this.fail(message.text + ":" + messages[i].level);
+    }
+
+    WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
+    this.takeControl();
+};
+
+/**
+ * Serializes array of uiSourceCodes to string.
+ * @param {Array.<WebInspectorUISourceCode>} uiSourceCodes
+ * @return {string}
+ */
+TestSuite.prototype.uiSourceCodesToString_ = function(uiSourceCodes)
+{
+    var names = [];
+    for (var i = 0; i < uiSourceCodes.length; i++)
+        names.push('"' + uiSourceCodes[i].url + '"');
+    return names.join(",");
+};
+
+
+/**
+ * Returns all loaded non anonymous uiSourceCodes.
+ * @return {Array.<WebInspectorUISourceCode>}
+ */
+TestSuite.prototype.nonAnonymousUISourceCodes_ = function()
+{
+    function filterOutAnonymous(uiSourceCode)
+    {
+        return !!uiSourceCode.url;
+    }
+
+    function filterOutService(uiSourceCode)
+    {
+        return !uiSourceCode.project().isServiceProject();
+    }
+
+    var uiSourceCodes = WebInspector.workspace.uiSourceCodes();
+    uiSourceCodes = uiSourceCodes.filter(filterOutService);
+    return uiSourceCodes.filter(filterOutAnonymous);
+};
+
+
+/*
+ * Evaluates the code in the console as if user typed it manually and invokes
+ * the callback when the result message is received and added to the console.
+ * @param {string} code
+ * @param {function(string)} callback
+ */
+TestSuite.prototype.evaluateInConsole_ = function(code, callback)
+{
+    WebInspector.showConsole();
+    WebInspector.consoleView.prompt.text = code;
+    WebInspector.consoleView.promptElement.dispatchEvent(TestSuite.createKeyEvent("Enter"));
+
+    this.addSniffer(WebInspector.ConsoleView.prototype, "_appendConsoleMessage",
+        function(commandResult) {
+            callback(commandResult.toMessageElement().textContent);
+        });
+};
+
+
+/**
+ * Checks that all expected scripts are present in the scripts list
+ * in the Scripts panel.
+ * @param {Array.<string>} expected Regular expressions describing
+ *     expected script names.
+ * @return {boolean} Whether all the scripts are in "scripts-files" select
+ *     box
+ */
+TestSuite.prototype._scriptsAreParsed = function(expected)
+{
+    var uiSourceCodes = this.nonAnonymousUISourceCodes_();
+    // Check that at least all the expected scripts are present.
+    var missing = expected.slice(0);
+    for (var i = 0; i < uiSourceCodes.length; ++i) {
+        for (var j = 0; j < missing.length; ++j) {
+            if (uiSourceCodes[i].name().search(missing[j]) !== -1) {
+                missing.splice(j, 1);
+                break;
+            }
+        }
+    }
+    return missing.length === 0;
+};
+
+
+/**
+ * Waits for script pause, checks expectations, and invokes the callback.
+ * @param {function():void} callback
+ */
+TestSuite.prototype._waitForScriptPause = function(callback)
+{
+    function pauseListener(event) {
+        WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, pauseListener, this);
+        callback();
+    }
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, pauseListener, this);
+};
+
+
+/**
+ * Waits until all the scripts are parsed and asynchronously executes the code
+ * in the inspected page.
+ */
+TestSuite.prototype._executeCodeWhenScriptsAreParsed = function(code, expectedScripts)
+{
+    var test = this;
+
+    function executeFunctionInInspectedPage() {
+        // Since breakpoints are ignored in evals' calculate() function is
+        // execute after zero-timeout so that the breakpoint is hit.
+        test.evaluateInConsole_(
+            'setTimeout("' + code + '" , 0)',
+            function(resultText) {
+                test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText + ". Code: " + code);
+            });
+    }
+
+    test._waitUntilScriptsAreParsed(expectedScripts, executeFunctionInInspectedPage);
+};
+
+
+/**
+ * Waits until all the scripts are parsed and invokes the callback.
+ */
+TestSuite.prototype._waitUntilScriptsAreParsed = function(expectedScripts, callback)
+{
+    var test = this;
+
+    function waitForAllScripts() {
+        if (test._scriptsAreParsed(expectedScripts))
+            callback();
+        else
+            test.addSniffer(WebInspector.panels.scripts, "_addUISourceCode", waitForAllScripts);
+    }
+
+    waitForAllScripts();
+};
+
+
+/**
+ * Key event with given key identifier.
+ */
+TestSuite.createKeyEvent = function(keyIdentifier)
+{
+    var evt = document.createEvent("KeyboardEvent");
+    evt.initKeyboardEvent("keydown", true /* can bubble */, true /* can cancel */, null /* view */, keyIdentifier, "");
+    return evt;
+};
+
+
+/**
+ * Test runner for the test suite.
+ */
+var uiTests = {};
+
+
+/**
+ * Run each test from the test suit on a fresh instance of the suite.
+ */
+uiTests.runAllTests = function()
+{
+    // For debugging purposes.
+    for (var name in TestSuite.prototype) {
+        if (name.substring(0, 4) === "test" && typeof TestSuite.prototype[name] === "function")
+            uiTests.runTest(name);
+    }
+};
+
+
+/**
+ * Run specified test on a fresh instance of the test suite.
+ * @param {string} name Name of a test method from TestSuite class.
+ */
+uiTests.runTest = function(name)
+{
+    if (uiTests._populatedInterface)
+        new TestSuite().runTest(name);
+    else
+        uiTests._pendingTestName = name;
+};
+
+(function() {
+
+function runTests()
+{
+    uiTests._populatedInterface = true;
+    var name = uiTests._pendingTestName;
+    delete uiTests._pendingTestName;
+    if (name)
+        new TestSuite().runTest(name);
+}
+
+var oldLoadCompleted = InspectorFrontendAPI.loadCompleted;
+InspectorFrontendAPI.loadCompleted = function()
+{
+    oldLoadCompleted.call(InspectorFrontendAPI);
+    runTests();
+}
+
+})();
+
+}
diff --git a/Source/devtools/front_end/TextEditor.js b/Source/devtools/front_end/TextEditor.js
new file mode 100644
index 0000000..157658b
--- /dev/null
+++ b/Source/devtools/front_end/TextEditor.js
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2010 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.
+ */
+
+/**
+ * @interface
+ */
+WebInspector.TextEditor = function() { };
+
+WebInspector.TextEditor.Events = {
+    GutterClick: "gutterClick"
+};
+
+/** @typedef {{lineNumber: number, event: Event}} */
+WebInspector.TextEditor.GutterClickEventData;
+
+WebInspector.TextEditor.prototype = {
+
+    undo: function() { },
+
+    redo: function() { },
+
+    /**
+     * @return {boolean}
+     */
+    isClean: function() { },
+
+    markClean: function() { },
+
+    /*
+     * @param {number} lineNumber
+     * @param {number} column
+     * @return {?{x: number, y: number, height: number}}
+     */
+    cursorPositionToCoordinates: function(lineNumber, column) { return null; },
+
+    /**
+     * @param {number} x
+     * @param {number} y
+     * @return {?WebInspector.TextRange}
+     */
+    coordinatesToCursorPosition: function(x, y) { return null; },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @return {?{startColumn: number, endColumn: number, type: string}}
+     */
+    tokenAtTextPosition: function(lineNumber, column) { return null; },
+
+    /**
+     * @param {string} mimeType
+     */
+    set mimeType(mimeType) { },
+
+    /**
+     * @param {boolean} readOnly
+     */
+    setReadOnly: function(readOnly) { },
+
+    /**
+     * @return {boolean}
+     */
+    readOnly: function() { },
+
+    /**
+     * @return {Element}
+     */
+    defaultFocusedElement: function() { },
+
+    /**
+     * @param {string} regex
+     * @param {string} cssClass
+     * @return {Object}
+     */
+    highlightRegex: function(regex, cssClass) { },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} cssClass
+     * @return {Object}
+     */
+    highlightRange: function(range, cssClass) { },
+
+    /**
+     * @param {Object} highlightDescriptor
+     */
+    removeHighlight: function(highlightDescriptor) { },
+
+    /**
+     * @param {number} lineNumber
+     */
+    revealLine: function(lineNumber) { },
+
+    /**
+     * @param {number} lineNumber
+     * @param {boolean} disabled
+     * @param {boolean} conditional
+     */
+    addBreakpoint: function(lineNumber, disabled, conditional) { },
+
+    /**
+     * @param {number} lineNumber
+     */
+    removeBreakpoint: function(lineNumber) { },
+
+    /**
+     * @param {number} lineNumber
+     */
+    setExecutionLine: function(lineNumber) { },
+
+    clearExecutionLine: function() { },
+
+    /**
+     * @param {number} lineNumber
+     * @param {Element} element
+     */
+    addDecoration: function(lineNumber, element) { },
+
+    /**
+     * @param {number} lineNumber
+     * @param {Element} element
+     */
+    removeDecoration: function(lineNumber, element) { },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     */
+    markAndRevealRange: function(range) { },
+
+    /**
+     * @param {number} lineNumber
+     */
+    highlightLine: function(lineNumber) { },
+
+    clearLineHighlight: function() { },
+
+    /**
+     * @return {Array.<Element>}
+     */
+    elementsToRestoreScrollPositionsFor: function() { },
+
+    /**
+     * @param {WebInspector.TextEditor} textEditor
+     */
+    inheritScrollPositions: function(textEditor) { },
+
+    beginUpdates: function() { },
+
+    endUpdates: function() { },
+
+    onResize: function() { },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} text
+     * @return {WebInspector.TextRange}
+     */
+    editRange: function(range, text) { },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollToLine: function(lineNumber) { },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    selection: function() { },
+
+    /**
+     * @return {WebInspector.TextRange?}
+     */
+    lastSelection: function() { },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    setSelection: function(textRange) { },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {string}
+     */
+    copyRange: function(range) { },
+
+    /**
+     * @param {string} text 
+     */
+    setText: function(text) { },
+
+    /**
+     * @return {string}
+     */
+    text: function() { },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    range: function() { },
+
+    /**
+     * @param {number} lineNumber
+     * @return {string}
+     */
+    line: function(lineNumber) { },
+
+    /**
+     * @return {number}
+     */
+    get linesCount() { },
+
+    /**
+     * @param {number} line
+     * @param {string} name  
+     * @param {Object?} value  
+     */
+    setAttribute: function(line, name, value) { },
+
+    /**
+     * @param {number} line
+     * @param {string} name  
+     * @return {Object|null} value  
+     */
+    getAttribute: function(line, name) { },
+
+    /**
+     * @param {number} line
+     * @param {string} name
+     */
+    removeAttribute: function(line, name) { },
+
+    wasShown: function() { },
+
+    willHide: function() { }
+}
+
+/**
+ * @interface
+ */
+WebInspector.TextEditorDelegate = function()
+{
+}
+
+WebInspector.TextEditorDelegate.prototype = {
+    /**
+     * @param {WebInspector.TextRange} oldRange
+     * @param {WebInspector.TextRange} newRange
+     */
+    onTextChanged: function(oldRange, newRange) { },
+
+    /**
+     * @param {WebInspector.TextRange} textRange
+     */
+    selectionChanged: function(textRange) { },
+
+    /**
+     * @param {number} lineNumber
+     */
+    scrollChanged: function(lineNumber) { },
+
+    /**
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {number} lineNumber
+     */
+    populateLineGutterContextMenu: function(contextMenu, lineNumber) { },
+
+    /**
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @param {number} lineNumber
+     */
+    populateTextAreaContextMenu: function(contextMenu, lineNumber) { },
+
+    /**
+     * @param {string} hrefValue
+     * @param {boolean} isExternal
+     * @return {Element}
+     */
+    createLink: function(hrefValue, isExternal) { }
+}
diff --git a/Source/devtools/front_end/TextEditorHighlighter.js b/Source/devtools/front_end/TextEditorHighlighter.js
new file mode 100644
index 0000000..c25adf7
--- /dev/null
+++ b/Source/devtools/front_end/TextEditorHighlighter.js
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2009 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.TextEditorHighlighter = function(textModel, damageCallback)
+{
+    this._textModel = textModel;
+    this._mimeType = "text/html";
+    this._tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer(this._mimeType);
+    this._damageCallback = damageCallback;
+    this._highlightChunkLimit = 1000;
+    this._highlightLineLimit = 500;
+}
+
+WebInspector.TextEditorHighlighter._MaxLineCount = 10000;
+
+WebInspector.TextEditorHighlighter.prototype = {
+
+    get mimeType()
+    {
+        return this._mimeType;
+    },
+
+    /**
+     * @param {string} mimeType
+     */
+    set mimeType(mimeType)
+    {
+        var tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer(mimeType);
+        if (tokenizer) {
+            this._tokenizer = tokenizer;
+            this._mimeType = mimeType;
+        }
+    },
+
+    set highlightChunkLimit(highlightChunkLimit)
+    {
+        this._highlightChunkLimit = highlightChunkLimit;
+    },
+
+    /**
+     * @param {number} highlightLineLimit
+     */
+    setHighlightLineLimit: function(highlightLineLimit)
+    {
+        this._highlightLineLimit = highlightLineLimit;
+    },
+
+    /**
+     * @param {boolean=} forceRun
+     */
+    highlight: function(endLine, forceRun)
+    {
+        if (this._textModel.linesCount > WebInspector.TextEditorHighlighter._MaxLineCount)
+            return;
+
+        // First check if we have work to do.
+        var state = this._textModel.getAttribute(endLine - 1, "highlight");
+        if (state && state.postConditionStringified) {
+            // Last line is highlighted, just exit.
+            return;
+        }
+
+        this._requestedEndLine = endLine;
+
+        if (this._highlightTimer && !forceRun) {
+            // There is a timer scheduled, it will catch the new job based on the new endLine set.
+            return;
+        }
+
+        // We will be highlighting. First rewind to the last highlighted line to gain proper highlighter context.
+        var startLine = endLine;
+        while (startLine > 0) {
+            state = this._textModel.getAttribute(startLine - 1, "highlight");
+            if (state && state.postConditionStringified)
+                break;
+            startLine--;
+        }
+
+        // Do small highlight synchronously. This will provide instant highlight on PageUp / PageDown, gentle scrolling.
+        this._highlightInChunks(startLine, endLine);
+    },
+
+    updateHighlight: function(startLine, endLine)
+    {
+        if (this._textModel.linesCount > WebInspector.TextEditorHighlighter._MaxLineCount)
+            return;
+
+        // Start line was edited, we should highlight everything until endLine.
+        this._clearHighlightState(startLine);
+
+        if (startLine) {
+            var state = this._textModel.getAttribute(startLine - 1, "highlight");
+            if (!state || !state.postConditionStringified) {
+                // Highlighter did not reach this point yet, nothing to update. It will reach it on subsequent timer tick and do the job.
+                return false;
+            }
+        }
+
+        var restored = this._highlightLines(startLine, endLine);
+        if (!restored) {
+            for (var i = this._lastHighlightedLine; i < this._textModel.linesCount; ++i) {
+                var state = this._textModel.getAttribute(i, "highlight");
+                if (!state && i > endLine)
+                    break;
+                this._textModel.setAttribute(i, "highlight-outdated", state);
+                this._textModel.removeAttribute(i, "highlight");
+            }
+
+            if (this._highlightTimer) {
+                clearTimeout(this._highlightTimer);
+                this._requestedEndLine = endLine;
+                this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, this._lastHighlightedLine, this._requestedEndLine), 10);
+            }
+        }
+        return restored;
+    },
+
+    _highlightInChunks: function(startLine, endLine)
+    {
+        delete this._highlightTimer;
+
+        // First we always check if we have work to do. Could be that user scrolled back and we can quit.
+        var state = this._textModel.getAttribute(this._requestedEndLine - 1, "highlight");
+        if (state && state.postConditionStringified)
+            return;
+
+        if (this._requestedEndLine !== endLine) {
+            // User keeps updating the job in between of our timer ticks. Just reschedule self, don't eat CPU (they must be scrolling).
+            this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, startLine, this._requestedEndLine), 100);
+            return;
+        }
+
+        // The textModel may have been already updated.
+        if (this._requestedEndLine > this._textModel.linesCount)
+            this._requestedEndLine = this._textModel.linesCount;
+
+        this._highlightLines(startLine, this._requestedEndLine);
+
+        // Schedule tail highlight if necessary.
+        if (this._lastHighlightedLine < this._requestedEndLine)
+            this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, this._lastHighlightedLine, this._requestedEndLine), 10);
+    },
+
+    _highlightLines: function(startLine, endLine)
+    {
+        // Restore highlighter context taken from previous line.
+        var state = this._textModel.getAttribute(startLine - 1, "highlight");
+        var postConditionStringified = state ? state.postConditionStringified : JSON.stringify(this._tokenizer.createInitialCondition());
+
+        var tokensCount = 0;
+        for (var lineNumber = startLine; lineNumber < endLine; ++lineNumber) {
+            state = this._selectHighlightState(lineNumber, postConditionStringified);
+            if (state.postConditionStringified) {
+                // This line is already highlighted.
+                postConditionStringified = state.postConditionStringified;
+            } else {
+                var lastHighlightedColumn = 0;
+                if (state.midConditionStringified) {
+                    lastHighlightedColumn = state.lastHighlightedColumn;
+                    postConditionStringified = state.midConditionStringified;
+                }
+
+                var line = this._textModel.line(lineNumber);
+                this._tokenizer.line = line;
+                this._tokenizer.condition = JSON.parse(postConditionStringified);
+
+                // Highlight line.
+                state.ranges = state.ranges || [];
+                state.braces = state.braces || [];
+                do {
+                    var newColumn = this._tokenizer.nextToken(lastHighlightedColumn);
+                    var tokenType = this._tokenizer.tokenType;
+                    if (tokenType && lastHighlightedColumn < this._highlightLineLimit) {
+                        if (tokenType === "brace-start" || tokenType === "brace-end" || tokenType === "block-start" || tokenType === "block-end") {
+                            state.braces.push({
+                                startColumn: lastHighlightedColumn,
+                                endColumn: newColumn - 1,
+                                token: tokenType
+                            });
+                        } else {
+                            state.ranges.push({
+                                startColumn: lastHighlightedColumn,
+                                endColumn: newColumn - 1,
+                                token: tokenType
+                            });
+                        }
+                    }
+                    lastHighlightedColumn = newColumn;
+                    if (++tokensCount > this._highlightChunkLimit)
+                        break;
+                } while (lastHighlightedColumn < line.length);
+
+                postConditionStringified = JSON.stringify(this._tokenizer.condition);
+
+                if (lastHighlightedColumn < line.length) {
+                    // Too much work for single chunk - exit.
+                    state.lastHighlightedColumn = lastHighlightedColumn;
+                    state.midConditionStringified = postConditionStringified;
+                    break;
+                } else {
+                    delete state.lastHighlightedColumn;
+                    delete state.midConditionStringified;
+                    state.postConditionStringified = postConditionStringified;
+                }
+            }
+
+            var nextLineState = this._textModel.getAttribute(lineNumber + 1, "highlight");
+            if (nextLineState && nextLineState.preConditionStringified === state.postConditionStringified) {
+                // Following lines are up to date, no need re-highlight.
+                ++lineNumber;
+                this._damageCallback(startLine, lineNumber);
+
+                // Advance the "pointer" to the last highlighted line within the given chunk.
+                for (; lineNumber < endLine; ++lineNumber) {
+                    state = this._textModel.getAttribute(lineNumber, "highlight");
+                    if (!state || !state.postConditionStringified)
+                        break;
+                }
+                this._lastHighlightedLine = lineNumber;
+                return true;
+            }
+        }
+
+        this._damageCallback(startLine, lineNumber);
+        this._lastHighlightedLine = lineNumber;
+        return false;
+    },
+
+    _selectHighlightState: function(lineNumber, preConditionStringified)
+    {
+        var state = this._textModel.getAttribute(lineNumber, "highlight");
+        if (state && state.preConditionStringified === preConditionStringified)
+            return state;
+
+        var outdatedState = this._textModel.getAttribute(lineNumber, "highlight-outdated");
+        if (outdatedState && outdatedState.preConditionStringified === preConditionStringified) {
+            // Swap states.
+            this._textModel.setAttribute(lineNumber, "highlight", outdatedState);
+            this._textModel.setAttribute(lineNumber, "highlight-outdated", state);
+            return outdatedState;
+        }
+
+        if (state)
+            this._textModel.setAttribute(lineNumber, "highlight-outdated", state);
+
+        state = {};
+        state.preConditionStringified = preConditionStringified;
+        this._textModel.setAttribute(lineNumber, "highlight", state);
+        return state;
+    },
+
+    _clearHighlightState: function(lineNumber)
+    {
+        this._textModel.removeAttribute(lineNumber, "highlight");
+        this._textModel.removeAttribute(lineNumber, "highlight-outdated");
+    }
+}
diff --git a/Source/devtools/front_end/TextEditorModel.js b/Source/devtools/front_end/TextEditorModel.js
new file mode 100644
index 0000000..a7bb56e
--- /dev/null
+++ b/Source/devtools/front_end/TextEditorModel.js
@@ -0,0 +1,893 @@
+/*
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {number} startLine
+ * @param {number} startColumn
+ * @param {number} endLine
+ * @param {number} endColumn
+ */
+WebInspector.TextRange = function(startLine, startColumn, endLine, endColumn)
+{
+    this.startLine = startLine;
+    this.startColumn = startColumn;
+    this.endLine = endLine;
+    this.endColumn = endColumn;
+}
+
+WebInspector.TextRange.createFromLocation = function(line, column)
+{
+    return new WebInspector.TextRange(line, column, line, column);
+}
+
+/**
+ * @param {Object} serializedTextRange
+ * @return {WebInspector.TextRange}
+ */
+WebInspector.TextRange.fromObject = function (serializedTextRange)
+{
+    return new WebInspector.TextRange(serializedTextRange.startLine, serializedTextRange.startColumn, serializedTextRange.endLine, serializedTextRange.endColumn);
+}
+
+WebInspector.TextRange.prototype = {
+    /**
+     * @return {boolean}
+     */
+    isEmpty: function()
+    {
+        return this.startLine === this.endLine && this.startColumn === this.endColumn;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {boolean}
+     */
+    immediatelyPrecedes: function(range)
+    {
+        if (!range)
+            return false;
+        return this.endLine === range.startLine && this.endColumn === range.startColumn;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {boolean}
+     */
+    immediatelyFollows: function(range)
+    {
+        if (!range)
+            return false;
+        return range.immediatelyPrecedes(this);
+    },
+
+    /**
+     * @return {number}
+     */
+    get linesCount()
+    {
+        return this.endLine - this.startLine;
+    },
+
+    collapseToEnd: function()
+    {
+        return new WebInspector.TextRange(this.endLine, this.endColumn, this.endLine, this.endColumn);
+    },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    normalize: function()
+    {
+        if (this.startLine > this.endLine || (this.startLine === this.endLine && this.startColumn > this.endColumn))
+            return new WebInspector.TextRange(this.endLine, this.endColumn, this.startLine, this.startColumn);
+        else
+            return this.clone();
+    },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    clone: function()
+    {
+        return new WebInspector.TextRange(this.startLine, this.startColumn, this.endLine, this.endColumn);
+    },
+
+    /**
+     * @return {Object}
+     */
+    serializeToObject: function()
+    {
+        var serializedTextRange = {};
+        serializedTextRange.startLine = this.startLine;
+        serializedTextRange.startColumn = this.startColumn;
+        serializedTextRange.endLine = this.endLine;
+        serializedTextRange.endColumn = this.endColumn;
+        return serializedTextRange;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} other
+     * @return {number}
+     */
+    compareTo: function(other)
+    {
+        if (this.startLine > other.startLine)
+            return 1;
+        if (this.startLine < other.startLine)
+            return -1;
+        if (this.startColumn > other.startColumn)
+            return 1;
+        if (this.startColumn < other.startColumn)
+            return -1;
+        return 0;
+    },
+
+    /**
+     * @param {number} lineOffset
+     * @return {WebInspector.TextRange}
+     */
+    shift: function(lineOffset)
+    {
+        return new WebInspector.TextRange(this.startLine + lineOffset, this.startColumn, this.endLine + lineOffset, this.endColumn);
+    },
+
+    toString: function()
+    {
+        return JSON.stringify(this);
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextRange} newRange
+ * @param {string} originalText
+ * @param {WebInspector.TextRange} originalSelection
+ */
+WebInspector.TextEditorCommand = function(newRange, originalText, originalSelection)
+{
+    this.newRange = newRange;
+    this.originalText = originalText;
+    this.originalSelection = originalSelection;
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.TextEditorModel = function()
+{
+    this._lines = [""];
+    this._attributes = [];
+    /** @type {Array.<WebInspector.TextEditorCommand>} */
+    this._undoStack = [];
+    this._noPunctuationRegex = /[^ !%&()*+,-.:;<=>?\[\]\^{|}~]+/;
+    this._lineBreak = "\n";
+}
+
+WebInspector.TextEditorModel.Events = {
+    TextChanged: "TextChanged"
+}
+
+WebInspector.TextEditorModel.endsWithBracketRegex = /[{(\[]\s*$/;
+
+WebInspector.TextEditorModel.prototype = {
+    /**
+     * @return {boolean}
+     */
+    isClean: function()
+    {
+        return this._cleanState === this._undoStack.length;
+    },
+
+    markClean: function()
+    {
+        this._cleanState = this._undoStack.length;
+    },
+
+    /**
+     * @return {number}
+     */
+    get linesCount()
+    {
+        return this._lines.length;
+    },
+
+    /**
+     * @return {string}
+     */
+    text: function()
+    {
+        return this._lines.join(this._lineBreak);
+    },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    range: function()
+    {
+        return new WebInspector.TextRange(0, 0, this._lines.length - 1, this._lines[this._lines.length - 1].length);
+    },
+
+    /**
+     * @return {string}
+     */
+    get lineBreak()
+    {
+        return this._lineBreak;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {string}
+     */
+    line: function(lineNumber)
+    {
+        if (lineNumber >= this._lines.length)
+            throw "Out of bounds:" + lineNumber;
+        return this._lines[lineNumber];
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @return {number}
+     */
+    lineLength: function(lineNumber)
+    {
+        return this._lines[lineNumber].length;
+    },
+
+    /**
+     * @param {string} text 
+     */
+    setText: function(text)
+    {
+        this._resetUndoStack();
+        text = text || "";
+        var range = this.range();
+        this._lineBreak = /\r\n/.test(text) ? "\r\n" : "\n";
+        var newRange = this._innerSetText(range, text);
+        this.dispatchEventToListeners(WebInspector.TextEditorModel.Events.TextChanged, { oldRange: range, newRange: newRange});
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {boolean}
+     */
+    _rangeHasOneCharacter: function(range)
+    {
+        if (range.startLine === range.endLine && range.endColumn - range.startColumn === 1)
+            return true;
+        if (range.endLine - range.startLine === 1 && range.endColumn === 0 && range.startColumn === this.lineLength(range.startLine))
+            return true;
+        return false;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} text
+     * @param {WebInspector.TextRange=} originalSelection
+     * @return {boolean}
+     */
+    _isEditRangeUndoBoundary: function(range, text, originalSelection)
+    {
+        if (originalSelection && !originalSelection.isEmpty())
+            return true;
+        if (text)
+            return text.length > 1 || !range.isEmpty();
+        return !this._rangeHasOneCharacter(range);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} text
+     * @return {boolean}
+     */
+    _isEditRangeAdjacentToLastCommand: function(range, text)
+    {
+        if (!this._lastCommand)
+            return true;
+        if (!text) {
+            // FIXME: Distinguish backspace and delete in lastCommand.
+            return this._lastCommand.newRange.immediatelyPrecedes(range) || this._lastCommand.newRange.immediatelyFollows(range);
+        }
+        return text.indexOf("\n") === -1 && this._lastCommand.newRange.immediatelyPrecedes(range);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} text
+     * @param {WebInspector.TextRange=} originalSelection
+     * @return {WebInspector.TextRange}
+     */
+    editRange: function(range, text, originalSelection)
+    {
+        var undoBoundary = this._isEditRangeUndoBoundary(range, text, originalSelection);
+        if (undoBoundary || !this._isEditRangeAdjacentToLastCommand(range, text))
+            this._markUndoableState();
+        var newRange = this._innerEditRange(range, text, originalSelection);
+        if (undoBoundary)
+            this._markUndoableState();
+        return newRange;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} text
+     * @param {WebInspector.TextRange=} originalSelection
+     * @return {WebInspector.TextRange}
+     */
+    _innerEditRange: function(range, text, originalSelection)
+    {
+        var originalText = this.copyRange(range);
+        var newRange = this._innerSetText(range, text);
+        this._lastCommand = this._pushUndoableCommand(newRange, originalText, originalSelection || range);
+        this.dispatchEventToListeners(WebInspector.TextEditorModel.Events.TextChanged, { oldRange: range, newRange: newRange, editRange: true });
+        return newRange;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {string} text
+     * @return {WebInspector.TextRange}
+     */
+    _innerSetText: function(range, text)
+    {
+        this._eraseRange(range);
+        if (text === "")
+            return new WebInspector.TextRange(range.startLine, range.startColumn, range.startLine, range.startColumn);
+
+        var newLines = text.split(/\r?\n/);
+
+        var prefix = this._lines[range.startLine].substring(0, range.startColumn);
+        var suffix = this._lines[range.startLine].substring(range.startColumn);
+
+        var postCaret = prefix.length;
+        // Insert text.
+        if (newLines.length === 1) {
+            this._setLine(range.startLine, prefix + newLines[0] + suffix);
+            postCaret += newLines[0].length;
+        } else {
+            this._setLine(range.startLine, prefix + newLines[0]);
+            this._insertLines(range, newLines);
+            this._setLine(range.startLine + newLines.length - 1, newLines[newLines.length - 1] + suffix);
+            postCaret = newLines[newLines.length - 1].length;
+        }
+
+        return new WebInspector.TextRange(range.startLine, range.startColumn,
+                                          range.startLine + newLines.length - 1, postCaret);
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @param {Array.<string>} newLines
+     */
+    _insertLines: function(range, newLines)
+    {
+        var lines = new Array(this._lines.length + newLines.length - 1);
+        for (var i = 0; i <= range.startLine; ++i)
+            lines[i] = this._lines[i];
+        // Line at [0] is already set via setLine.
+        for (var i = 1; i < newLines.length; ++i)
+            lines[range.startLine + i] = newLines[i];
+        for (var i = range.startLine + newLines.length; i < lines.length; ++i)
+            lines[i] = this._lines[i - newLines.length + 1];
+        this._lines = lines;
+
+        // Adjust attributes, attributes move with the first character of line.
+        var attributes = new Array(lines.length);
+        var insertionIndex = range.startColumn ? range.startLine + 1 : range.startLine;
+        for (var i = 0; i < insertionIndex; ++i)
+            attributes[i] = this._attributes[i];
+        for (var i = insertionIndex + newLines.length - 1; i < attributes.length; ++i)
+            attributes[i] = this._attributes[i - newLines.length + 1];
+        this._attributes = attributes;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     */
+    _eraseRange: function(range)
+    {
+        if (range.isEmpty())
+            return;
+
+        var prefix = this._lines[range.startLine].substring(0, range.startColumn);
+        var suffix = this._lines[range.endLine].substring(range.endColumn);
+
+        if (range.endLine > range.startLine) {
+            this._lines.splice(range.startLine + 1, range.endLine - range.startLine);
+            // Adjust attributes, attributes move with the first character of line.
+            this._attributes.splice(range.startColumn ? range.startLine + 1 : range.startLine, range.endLine - range.startLine);
+        }
+        this._setLine(range.startLine, prefix + suffix);
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {string} text
+     */
+    _setLine: function(lineNumber, text)
+    {
+        this._lines[lineNumber] = text;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @return {WebInspector.TextRange}
+     */
+    wordRange: function(lineNumber, column)
+    {
+        return new WebInspector.TextRange(lineNumber, this.wordStart(lineNumber, column, true), lineNumber, this.wordEnd(lineNumber, column, true));
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @param {boolean} gapless
+     * @return {number}
+     */
+    wordStart: function(lineNumber, column, gapless)
+    {
+        var line = this._lines[lineNumber];
+        var prefix = line.substring(0, column).split("").reverse().join("");
+        var prefixMatch = this._noPunctuationRegex.exec(prefix);
+        return prefixMatch && (!gapless || prefixMatch.index === 0) ? column - prefixMatch.index - prefixMatch[0].length : column;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @param {boolean} gapless
+     * @return {number}
+     */
+    wordEnd: function(lineNumber, column, gapless)
+    {
+        var line = this._lines[lineNumber];
+        var suffix = line.substring(column);
+        var suffixMatch = this._noPunctuationRegex.exec(suffix);
+        return suffixMatch && (!gapless || suffixMatch.index === 0) ? column + suffixMatch.index + suffixMatch[0].length : column;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {string}  
+     */
+    copyRange: function(range)
+    {
+        if (!range)
+            range = this.range();
+
+        var clip = [];
+        if (range.startLine === range.endLine) {
+            clip.push(this._lines[range.startLine].substring(range.startColumn, range.endColumn));
+            return clip.join(this._lineBreak);
+        }
+        clip.push(this._lines[range.startLine].substring(range.startColumn));
+        for (var i = range.startLine + 1; i < range.endLine; ++i)
+            clip.push(this._lines[i]);
+        clip.push(this._lines[range.endLine].substring(0, range.endColumn));
+        return clip.join(this._lineBreak);
+    },
+
+    /**
+     * @param {number} line
+     * @param {string} name  
+     * @param {Object?} value  
+     */
+    setAttribute: function(line, name, value)
+    {
+        var attrs = this._attributes[line];
+        if (!attrs) {
+            attrs = {};
+            this._attributes[line] = attrs;
+        }
+        attrs[name] = value;
+    },
+
+    /**
+     * @param {number} line
+     * @param {string} name  
+     * @return {Object|null} value  
+     */
+    getAttribute: function(line, name)
+    {
+        var attrs = this._attributes[line];
+        return attrs ? attrs[name] : null;
+    },
+
+    /**
+     * @param {number} line
+     * @param {string} name
+     */
+    removeAttribute: function(line, name)
+    {
+        var attrs = this._attributes[line];
+        if (attrs)
+            delete attrs[name];
+    },
+
+    /**
+     * @param {WebInspector.TextRange} newRange
+     * @param {string} originalText
+     * @param {WebInspector.TextRange} originalSelection
+     * @return {WebInspector.TextEditorCommand}
+     */
+    _pushUndoableCommand: function(newRange, originalText, originalSelection)
+    {
+        var command = new WebInspector.TextEditorCommand(newRange.clone(), originalText, originalSelection);
+        if (this._inUndo)
+            this._redoStack.push(command);
+        else {
+            if (!this._inRedo) {
+                this._redoStack = [];
+                if (typeof this._cleanState === "number" && this._cleanState > this._undoStack.length)
+                    delete this._cleanState;
+            }
+            this._undoStack.push(command);
+        }
+        return command;
+    },
+
+    /**
+     * @return {?WebInspector.TextRange}
+     */
+    undo: function()
+    {
+        if (!this._undoStack.length)
+            return null;
+
+        this._markRedoableState();
+
+        this._inUndo = true;
+        var range = this._doUndo(this._undoStack);
+        delete this._inUndo;
+
+        return range;
+    },
+
+    /**
+     * @return {WebInspector.TextRange}
+     */
+    redo: function()
+    {
+        if (!this._redoStack || !this._redoStack.length)
+            return null;
+        this._markUndoableState();
+
+        this._inRedo = true;
+        var range = this._doUndo(this._redoStack);
+        delete this._inRedo;
+
+        return range ? range.collapseToEnd() : null;
+    },
+
+    /**
+     * @param {Array.<WebInspector.TextEditorCommand>} stack
+     * @return {WebInspector.TextRange}
+     */
+    _doUndo: function(stack)
+    {
+        var range = null;
+        for (var i = stack.length - 1; i >= 0; --i) {
+            var command = stack[i];
+            stack.length = i;
+            this._innerEditRange(command.newRange, command.originalText);
+            range = command.originalSelection;
+            if (i > 0 && stack[i - 1].explicit)
+                return range;
+        }
+        return range;
+    },
+
+    _markUndoableState: function()
+    {
+        if (this._undoStack.length)
+            this._undoStack[this._undoStack.length - 1].explicit = true;
+    },
+
+    _markRedoableState: function()
+    {
+        if (this._redoStack.length)
+            this._redoStack[this._redoStack.length - 1].explicit = true;
+    },
+
+    _resetUndoStack: function()
+    {
+        delete this._cleanState;
+        this._undoStack = [];
+        this._redoStack = [];
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {WebInspector.TextRange}
+     */
+    indentLines: function(range)
+    {
+        this._markUndoableState();
+
+        var indent = WebInspector.settings.textEditorIndent.get();
+        var newRange = range.clone();
+        // Do not change a selection start position when it is at the beginning of a line
+        if (range.startColumn)
+            newRange.startColumn += indent.length;
+
+        var indentEndLine = range.endLine;
+        if (range.endColumn)
+            newRange.endColumn += indent.length;
+        else
+            indentEndLine--;
+
+        for (var lineNumber = range.startLine; lineNumber <= indentEndLine; lineNumber++)
+            this._innerEditRange(WebInspector.TextRange.createFromLocation(lineNumber, 0), indent);
+
+        return newRange;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {WebInspector.TextRange}
+     */
+    unindentLines: function(range)
+    {
+        this._markUndoableState();
+
+        var indent = WebInspector.settings.textEditorIndent.get();
+        var indentLength = indent === WebInspector.TextUtils.Indent.TabCharacter ? 4 : indent.length;
+        var lineIndentRegex = new RegExp("^ {1," + indentLength + "}");
+        var newRange = range.clone();
+
+        var indentEndLine = range.endLine;
+        if (!range.endColumn)
+            indentEndLine--;
+
+        for (var lineNumber = range.startLine; lineNumber <= indentEndLine; lineNumber++) {
+            var line = this.line(lineNumber);
+            var firstCharacter = line.charAt(0);
+            var lineIndentLength;
+
+            if (firstCharacter === " ")
+                lineIndentLength = line.match(lineIndentRegex)[0].length;
+            else if (firstCharacter === "\t")
+                lineIndentLength = 1;
+            else
+                continue;
+
+            this._innerEditRange(new WebInspector.TextRange(lineNumber, 0, lineNumber, lineIndentLength), "");
+
+            if (lineNumber === range.startLine)
+                newRange.startColumn = Math.max(0, newRange.startColumn - lineIndentLength);
+            if (lineNumber === range.endLine)
+                newRange.endColumn = Math.max(0, newRange.endColumn - lineIndentLength);
+        }
+
+        return newRange;
+    },
+
+    /**
+     * @param {number=} from
+     * @param {number=} to
+     * @return {WebInspector.TextEditorModel}
+     */
+    slice: function(from, to)
+    {
+        var textModel = new WebInspector.TextEditorModel();
+        textModel._lines = this._lines.slice(from, to);
+        textModel._lineBreak = this._lineBreak;
+        return textModel;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {WebInspector.TextRange}
+     */
+    growRangeLeft: function(range)
+    {
+        var result = range.clone();
+        if (result.startColumn)
+            --result.startColumn;
+        else if (result.startLine)
+            result.startColumn = this.lineLength(--result.startLine);
+        return result;
+    },
+
+    /**
+     * @param {WebInspector.TextRange} range
+     * @return {WebInspector.TextRange}
+     */
+    growRangeRight: function(range)
+    {
+        var result = range.clone();
+        if (result.endColumn < this.lineLength(result.endLine))
+            ++result.endColumn;
+        else if (result.endLine < this.linesCount) {
+            result.endColumn = 0;
+            ++result.endLine;
+        }
+        return result;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TextEditorModel} textModel
+ */
+WebInspector.TextEditorModel.BraceMatcher = function(textModel)
+{
+    this._textModel = textModel;
+}
+
+WebInspector.TextEditorModel.BraceMatcher.prototype = {
+    /**
+     * @param {number} lineNumber
+     * @return {Array.<{startColumn: number, endColumn: number, token: string}>}
+     */
+    _braceRanges: function(lineNumber)
+    {
+        if (lineNumber >= this._textModel.linesCount || lineNumber < 0)
+            return null;
+
+        var attribute = this._textModel.getAttribute(lineNumber, "highlight");
+        if (!attribute)
+            return null;
+        else
+            return attribute.braces;
+    },
+
+    /**
+     * @param {string} braceTokenLeft
+     * @param {string} braceTokenRight
+     * @return {boolean}
+     */
+    _matches: function(braceTokenLeft, braceTokenRight)
+    {
+        return ((braceTokenLeft === "brace-start" && braceTokenRight === "brace-end") || (braceTokenLeft === "block-start" && braceTokenRight === "block-end"));
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @param {number=} maxBraceIteration
+     * @return {?{lineNumber: number, column: number, token: string}}
+     */
+    findLeftCandidate: function(lineNumber, column, maxBraceIteration)
+    {
+        var braces = this._braceRanges(lineNumber);
+        if (!braces)
+            return null;
+
+        var braceIndex = braces.length - 1;
+        while (braceIndex >= 0 && braces[braceIndex].startColumn > column)
+            --braceIndex;
+
+        var brace = braceIndex >= 0 ? braces[braceIndex] : null;
+        if (brace && brace.startColumn === column && (brace.token === "block-end" || brace.token === "brace-end"))
+            --braceIndex;
+
+        var stack = [];
+        maxBraceIteration = maxBraceIteration || Number.MAX_VALUE;
+        while (--maxBraceIteration) {
+            if (braceIndex < 0) {
+                while ((braces = this._braceRanges(--lineNumber)) && !braces.length) {};
+                if (!braces)
+                    return null;
+                braceIndex = braces.length - 1;
+            }
+            brace = braces[braceIndex];
+            if (brace.token === "block-end" || brace.token === "brace-end")
+                stack.push(brace.token);
+            else if (stack.length === 0)
+                return {
+                    lineNumber: lineNumber,
+                    column: brace.startColumn,
+                    token: brace.token
+                };
+            else if (!this._matches(brace.token, stack.pop()))
+                return null;
+
+            --braceIndex;
+        }
+        return null;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @param {number=} maxBraceIteration
+     * @return {?{lineNumber: number, column: number, token: string}}
+     */
+    findRightCandidate: function(lineNumber, column, maxBraceIteration)
+    {
+        var braces = this._braceRanges(lineNumber);
+        if (!braces)
+            return null;
+
+        var braceIndex = 0;
+        while (braceIndex < braces.length && braces[braceIndex].startColumn < column)
+            ++braceIndex;
+
+        var brace = braceIndex < braces.length ? braces[braceIndex] : null;
+        if (brace && brace.startColumn === column && (brace.token === "block-start" || brace.token === "brace-start"))
+            ++braceIndex;
+
+        var stack = [];
+        maxBraceIteration = maxBraceIteration || Number.MAX_VALUE;
+        while (--maxBraceIteration) {
+            if (braceIndex >= braces.length) {
+                while ((braces = this._braceRanges(++lineNumber)) && !braces.length) {};
+                if (!braces)
+                    return null;
+                braceIndex = 0;
+            }
+            brace = braces[braceIndex];
+            if (brace.token === "block-start" || brace.token === "brace-start")
+                stack.push(brace.token);
+            else if (stack.length === 0)
+                return {
+                    lineNumber: lineNumber,
+                    column: brace.startColumn,
+                    token: brace.token
+                };
+            else if (!this._matches(stack.pop(), brace.token))
+                return null;
+            ++braceIndex;
+        }
+        return null;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} column
+     * @param {number=} maxBraceIteration
+     * @return {?{leftBrace: {lineNumber: number, column: number, token: string}, rightBrace: {lineNumber: number, column: number, token: string}}}
+     */
+    enclosingBraces: function(lineNumber, column, maxBraceIteration)
+    {
+        var leftBraceLocation = this.findLeftCandidate(lineNumber, column, maxBraceIteration);
+        if (!leftBraceLocation)
+            return null;
+
+        var rightBraceLocation = this.findRightCandidate(lineNumber, column, maxBraceIteration);
+        if (!rightBraceLocation)
+            return null;
+
+        if (!this._matches(leftBraceLocation.token, rightBraceLocation.token))
+            return null;
+
+        return {
+            leftBrace: leftBraceLocation,
+            rightBrace: rightBraceLocation
+        };
+    },
+}
diff --git a/Source/devtools/front_end/TextPrompt.js b/Source/devtools/front_end/TextPrompt.js
new file mode 100644
index 0000000..77443f8
--- /dev/null
+++ b/Source/devtools/front_end/TextPrompt.js
@@ -0,0 +1,940 @@
+/*
+ * Copyright (C) 2008 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @extends WebInspector.Object
+ * @implements {WebInspector.SuggestBoxDelegate}
+ * @param {function(Element, Range, boolean, function(!Array.<string>, number=))} completions
+ * @param {string=} stopCharacters
+ */
+WebInspector.TextPrompt = function(completions, stopCharacters)
+{
+    /**
+     * @type {Element|undefined}
+     */
+    this._proxyElement;
+    this._proxyElementDisplay = "inline-block";
+    this._loadCompletions = completions;
+    this._completionStopCharacters = stopCharacters || " =:[({;,!+-*/&|^<>.";
+    this._suggestForceable = true;
+}
+
+WebInspector.TextPrompt.Events = {
+    ItemApplied: "text-prompt-item-applied",
+    ItemAccepted: "text-prompt-item-accepted"
+};
+
+WebInspector.TextPrompt.prototype = {
+    get proxyElement()
+    {
+        return this._proxyElement;
+    },
+
+    setSuggestForceable: function(x)
+    {
+        this._suggestForceable = x;
+    },
+
+    setSuggestBoxEnabled: function(className)
+    {
+        this._suggestBoxClassName = className;
+    },
+
+    renderAsBlock: function()
+    {
+        this._proxyElementDisplay = "block";
+    },
+
+    /**
+     * Clients should never attach any event listeners to the |element|. Instead,
+     * they should use the result of this method to attach listeners for bubbling events.
+     *
+     * @param {Element} element
+     */
+    attach: function(element)
+    {
+        return this._attachInternal(element);
+    },
+
+    /**
+     * Clients should never attach any event listeners to the |element|. Instead,
+     * they should use the result of this method to attach listeners for bubbling events
+     * or the |blurListener| parameter to register a "blur" event listener on the |element|
+     * (since the "blur" event does not bubble.)
+     *
+     * @param {Element} element
+     * @param {function(Event)} blurListener
+     */
+    attachAndStartEditing: function(element, blurListener)
+    {
+        this._attachInternal(element);
+        this._startEditing(blurListener);
+        return this.proxyElement;
+    },
+
+    _attachInternal: function(element)
+    {
+        if (this.proxyElement)
+            throw "Cannot attach an attached TextPrompt";
+        this._element = element;
+
+        this._boundOnKeyDown = this.onKeyDown.bind(this);
+        this._boundOnMouseWheel = this.onMouseWheel.bind(this);
+        this._boundSelectStart = this._selectStart.bind(this);
+        this._proxyElement = element.ownerDocument.createElement("span");
+        this._proxyElement.style.display = this._proxyElementDisplay;
+        element.parentElement.insertBefore(this.proxyElement, element);
+        this.proxyElement.appendChild(element);
+        this._element.addStyleClass("text-prompt");
+        this._element.addEventListener("keydown", this._boundOnKeyDown, false);
+        this._element.addEventListener("mousewheel", this._boundOnMouseWheel, false);
+        this._element.addEventListener("selectstart", this._boundSelectStart, false);
+
+        if (typeof this._suggestBoxClassName === "string")
+            this._suggestBox = new WebInspector.SuggestBox(this, this._element, this._suggestBoxClassName);
+
+        return this.proxyElement;
+    },
+
+    detach: function()
+    {
+        this._removeFromElement();
+        this.proxyElement.parentElement.insertBefore(this._element, this.proxyElement);
+        this.proxyElement.parentElement.removeChild(this.proxyElement);
+        this._element.removeStyleClass("text-prompt");
+        this._element.removeEventListener("keydown", this._boundOnKeyDown, false);
+        this._element.removeEventListener("mousewheel", this._boundOnMouseWheel, false);
+        this._element.removeEventListener("selectstart", this._boundSelectStart, false);
+        delete this._proxyElement;
+        WebInspector.restoreFocusFromElement(this._element);
+    },
+
+    get text()
+    {
+        return this._element.textContent;
+    },
+
+    set text(x)
+    {
+        this._removeSuggestionAids();
+        if (!x) {
+            // Append a break element instead of setting textContent to make sure the selection is inside the prompt.
+            this._element.removeChildren();
+            this._element.appendChild(document.createElement("br"));
+        } else
+            this._element.textContent = x;
+
+        this.moveCaretToEndOfPrompt();
+        this._element.scrollIntoView();
+    },
+
+    _removeFromElement: function()
+    {
+        this.clearAutoComplete(true);
+        this._element.removeEventListener("keydown", this._boundOnKeyDown, false);
+        this._element.removeEventListener("selectstart", this._boundSelectStart, false);
+        if (this._isEditing)
+            this._stopEditing();
+        if (this._suggestBox)
+            this._suggestBox.removeFromElement();
+    },
+
+    _startEditing: function(blurListener)
+    {
+        this._isEditing = true;
+        this._element.addStyleClass("editing");
+        if (blurListener) {
+            this._blurListener = blurListener;
+            this._element.addEventListener("blur", this._blurListener, false);
+        }
+        this._oldTabIndex = this._element.tabIndex;
+        if (this._element.tabIndex < 0)
+            this._element.tabIndex = 0;
+        WebInspector.setCurrentFocusElement(this._element);
+    },
+
+    _stopEditing: function()
+    {
+        this._element.tabIndex = this._oldTabIndex;
+        if (this._blurListener)
+            this._element.removeEventListener("blur", this._blurListener, false);
+        this._element.removeStyleClass("editing");
+        delete this._isEditing;
+    },
+
+    _removeSuggestionAids: function()
+    {
+        this.clearAutoComplete();
+        this.hideSuggestBox();
+    },
+
+    _selectStart: function(event)
+    {
+        if (this._selectionTimeout)
+            clearTimeout(this._selectionTimeout);
+
+        this._removeSuggestionAids();
+
+        function moveBackIfOutside()
+        {
+            delete this._selectionTimeout;
+            if (!this.isCaretInsidePrompt() && window.getSelection().isCollapsed) {
+                this.moveCaretToEndOfPrompt();
+                this.autoCompleteSoon();
+            }
+        }
+
+        this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100);
+    },
+
+    /**
+     * @param {boolean=} force
+     */
+    defaultKeyHandler: function(event, force)
+    {
+        this.clearAutoComplete();
+        this.autoCompleteSoon(force);
+        return false;
+    },
+
+    onMouseWheel: function(event)
+    {
+        // Subclasses can implement. 
+    },
+
+    onKeyDown: function(event)
+    {
+        var handled = false;
+        var invokeDefault = true;
+
+        switch (event.keyIdentifier) {
+        case "Up":
+            handled = this.upKeyPressed(event);
+            break;
+        case "Down":
+            handled = this.downKeyPressed(event);
+            break;
+        case "PageUp":
+            handled = this.pageUpKeyPressed(event);
+            break;
+        case "PageDown":
+            handled = this.pageDownKeyPressed(event);
+            break;
+        case "U+0009": // Tab
+            handled = this.tabKeyPressed(event);
+            break;
+        case "Enter":
+            handled = this.enterKeyPressed(event);
+            break;
+        case "Left":
+        case "Home":
+            this._removeSuggestionAids();
+            invokeDefault = false;
+            break;
+        case "Right":
+        case "End":
+            if (this.isCaretAtEndOfPrompt())
+                handled = this.acceptAutoComplete();
+            else
+                this._removeSuggestionAids();
+            invokeDefault = false;
+            break;
+        case "U+001B": // Esc
+            if (this.isSuggestBoxVisible()) {
+                this._suggestBox.hide();
+                handled = true;
+            }
+            break;
+        case "U+0020": // Space
+            if (this._suggestForceable && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) {
+                this.defaultKeyHandler(event, true);
+                handled = true;
+            }
+            break;
+        case "Alt":
+        case "Meta":
+        case "Shift":
+        case "Control":
+            invokeDefault = false;
+            break;
+        }
+
+        if (!handled && invokeDefault)
+            handled = this.defaultKeyHandler(event);
+
+        if (handled)
+            event.consume(true);
+
+        return handled;
+    },
+
+    acceptAutoComplete: function()
+    {
+        var result = false;
+        if (this.isSuggestBoxVisible())
+            result = this._suggestBox.acceptSuggestion();
+        if (!result)
+            result = this.acceptSuggestion();
+
+        return result;
+    },
+
+    /**
+     * @param {boolean=} includeTimeout
+     */
+    clearAutoComplete: function(includeTimeout)
+    {
+        if (includeTimeout && this._completeTimeout) {
+            clearTimeout(this._completeTimeout);
+            delete this._completeTimeout;
+        }
+        delete this._waitingForCompletions;
+
+        if (!this.autoCompleteElement)
+            return;
+
+        if (this.autoCompleteElement.parentNode)
+            this.autoCompleteElement.parentNode.removeChild(this.autoCompleteElement);
+        delete this.autoCompleteElement;
+
+        if (!this._userEnteredRange || !this._userEnteredText)
+            return;
+
+        this._userEnteredRange.deleteContents();
+        this._element.normalize();
+
+        var userTextNode = document.createTextNode(this._userEnteredText);
+        this._userEnteredRange.insertNode(userTextNode);
+
+        var selectionRange = document.createRange();
+        selectionRange.setStart(userTextNode, this._userEnteredText.length);
+        selectionRange.setEnd(userTextNode, this._userEnteredText.length);
+
+        var selection = window.getSelection();
+        selection.removeAllRanges();
+        selection.addRange(selectionRange);
+
+        delete this._userEnteredRange;
+        delete this._userEnteredText;
+    },
+
+    /**
+     * @param {boolean=} force
+     */
+    autoCompleteSoon: function(force)
+    {
+        var immediately = this.isSuggestBoxVisible() || force;
+        if (!this._completeTimeout)
+            this._completeTimeout = setTimeout(this.complete.bind(this, true, force), immediately ? 0 : 250);
+    },
+
+    /**
+     * @param {boolean=} reverse
+     */
+    complete: function(auto, force, reverse)
+    {
+        this.clearAutoComplete(true);
+        var selection = window.getSelection();
+        if (!selection.rangeCount)
+            return;
+
+        var selectionRange = selection.getRangeAt(0);
+        var isEmptyInput = selectionRange.commonAncestorContainer === this._element; // this._element has no child Text nodes.
+
+        var shouldExit;
+
+        // Do not attempt to auto-complete an empty input in the auto mode (only on demand).
+        if (auto && isEmptyInput && !force)
+            shouldExit = true;
+        else if (!auto && !isEmptyInput && !selectionRange.commonAncestorContainer.isDescendant(this._element))
+            shouldExit = true;
+        else if (auto && !force && !this.isCaretAtEndOfPrompt() && !this.isSuggestBoxVisible())
+            shouldExit = true;
+        else if (!selection.isCollapsed)
+            shouldExit = true;
+        else if (!force) {
+            // BUG72018: Do not show suggest box if caret is followed by a non-stop character.
+            var wordSuffixRange = selectionRange.startContainer.rangeOfWord(selectionRange.endOffset, this._completionStopCharacters, this._element, "forward");
+            if (wordSuffixRange.toString().length)
+                shouldExit = true;
+        }
+        if (shouldExit) {
+            this.hideSuggestBox();
+            return;
+        }
+
+        var wordPrefixRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, this._completionStopCharacters, this._element, "backward");
+        this._waitingForCompletions = true;
+        this._loadCompletions(this.proxyElement, wordPrefixRange, force, this._completionsReady.bind(this, selection, auto, wordPrefixRange, !!reverse));
+    },
+
+    _boxForAnchorAtStart: function(selection, textRange)
+    {
+        var rangeCopy = selection.getRangeAt(0).cloneRange();
+        var anchorElement = document.createElement("span");
+        anchorElement.textContent = "\u200B";
+        textRange.insertNode(anchorElement);
+        var box = anchorElement.boxInWindow(window);
+        anchorElement.parentElement.removeChild(anchorElement);
+        selection.removeAllRanges();
+        selection.addRange(rangeCopy);
+        return box;
+    },
+
+    /**
+     * @param {Array.<string>} completions
+     * @param {number} wordPrefixLength
+     */
+    _buildCommonPrefix: function(completions, wordPrefixLength)
+    {
+        var commonPrefix = completions[0];
+        for (var i = 0; i < completions.length; ++i) {
+            var completion = completions[i];
+            var lastIndex = Math.min(commonPrefix.length, completion.length);
+            for (var j = wordPrefixLength; j < lastIndex; ++j) {
+                if (commonPrefix[j] !== completion[j]) {
+                    commonPrefix = commonPrefix.substr(0, j);
+                    break;
+                }
+            }
+        }
+        return commonPrefix;
+    },
+
+    /**
+     * @param {Selection} selection
+     * @param {boolean} auto
+     * @param {Range} originalWordPrefixRange
+     * @param {boolean} reverse
+     * @param {!Array.<string>} completions
+     * @param {number=} selectedIndex
+     */
+    _completionsReady: function(selection, auto, originalWordPrefixRange, reverse, completions, selectedIndex)
+    {
+        if (!this._waitingForCompletions || !completions.length) {
+            this.hideSuggestBox();
+            return;
+        }
+        delete this._waitingForCompletions;
+
+        var selectionRange = selection.getRangeAt(0);
+
+        var fullWordRange = document.createRange();
+        fullWordRange.setStart(originalWordPrefixRange.startContainer, originalWordPrefixRange.startOffset);
+        fullWordRange.setEnd(selectionRange.endContainer, selectionRange.endOffset);
+
+        if (originalWordPrefixRange.toString() + selectionRange.toString() != fullWordRange.toString())
+            return;
+
+        selectedIndex = selectedIndex || 0;
+
+        this._userEnteredRange = fullWordRange;
+        this._userEnteredText = fullWordRange.toString();
+
+        if (this._suggestBox)
+            this._suggestBox.updateSuggestions(this._boxForAnchorAtStart(selection, fullWordRange), completions, selectedIndex, !this.isCaretAtEndOfPrompt(), this._userEnteredText);
+
+        var wordPrefixLength = originalWordPrefixRange.toString().length;
+
+        if (auto) {
+            var completionText = completions[selectedIndex];
+            var commonPrefix = this._buildCommonPrefix(completions, wordPrefixLength);
+
+            this._commonPrefix = commonPrefix;
+        } else {
+            if (completions.length === 1) {
+                var completionText = completions[selectedIndex];
+                wordPrefixLength = completionText.length;
+            } else {
+                var commonPrefix = this._buildCommonPrefix(completions, wordPrefixLength);
+                wordPrefixLength = commonPrefix.length;
+
+                if (selection.isCollapsed)
+                    var completionText = completions[selectedIndex];
+                else {
+                    var currentText = fullWordRange.toString();
+
+                    var foundIndex = null;
+                    for (var i = 0; i < completions.length; ++i) {
+                        if (completions[i] === currentText)
+                            foundIndex = i;
+                    }
+
+                    var nextIndex = foundIndex + (reverse ? -1 : 1);
+                    if (foundIndex === null || nextIndex >= completions.length)
+                        var completionText = completions[selectedIndex];
+                    else if (nextIndex < 0)
+                        var completionText = completions[completions.length - 1];
+                    else
+                        var completionText = completions[nextIndex];
+                }
+            }
+        }
+
+        if (auto) {
+            if (this.isCaretAtEndOfPrompt()) {
+                this._userEnteredRange.deleteContents();
+                this._element.normalize();
+                var finalSelectionRange = document.createRange();
+                var prefixText = completionText.substring(0, wordPrefixLength);
+                var suffixText = completionText.substring(wordPrefixLength);
+
+                var prefixTextNode = document.createTextNode(prefixText);
+                fullWordRange.insertNode(prefixTextNode);
+
+                this.autoCompleteElement = document.createElement("span");
+                this.autoCompleteElement.className = "auto-complete-text";
+                this.autoCompleteElement.textContent = suffixText;
+
+                prefixTextNode.parentNode.insertBefore(this.autoCompleteElement, prefixTextNode.nextSibling);
+
+                finalSelectionRange.setStart(prefixTextNode, wordPrefixLength);
+                finalSelectionRange.setEnd(prefixTextNode, wordPrefixLength);
+                selection.removeAllRanges();
+                selection.addRange(finalSelectionRange);
+            }
+        } else
+            this._applySuggestion(completionText, completions.length > 1, originalWordPrefixRange);
+    },
+
+    _completeCommonPrefix: function()
+    {
+        if (!this.autoCompleteElement || !this._commonPrefix || !this._userEnteredText || !this._commonPrefix.startsWith(this._userEnteredText))
+            return;
+
+        if (!this.isSuggestBoxVisible()) {
+            this.acceptAutoComplete();
+            return;
+        }
+
+        this.autoCompleteElement.textContent = this._commonPrefix.substring(this._userEnteredText.length);
+        this.acceptSuggestion(true)
+    },
+
+    /**
+     * @param {string} completionText
+     * @param {boolean=} isIntermediateSuggestion
+     */
+    applySuggestion: function(completionText, isIntermediateSuggestion)
+    {
+        this._applySuggestion(completionText, isIntermediateSuggestion);
+    },
+
+    /**
+     * @param {string} completionText
+     * @param {boolean=} isIntermediateSuggestion
+     * @param {Range=} originalPrefixRange
+     */
+    _applySuggestion: function(completionText, isIntermediateSuggestion, originalPrefixRange)
+    {
+        var wordPrefixLength;
+        if (originalPrefixRange)
+            wordPrefixLength = originalPrefixRange.toString().length;
+        else
+            wordPrefixLength = this._userEnteredText ? this._userEnteredText.length : 0;
+
+        this._userEnteredRange.deleteContents();
+        this._element.normalize();
+        var finalSelectionRange = document.createRange();
+        var completionTextNode = document.createTextNode(completionText);
+        this._userEnteredRange.insertNode(completionTextNode);
+        if (this.autoCompleteElement && this.autoCompleteElement.parentNode) {
+            this.autoCompleteElement.parentNode.removeChild(this.autoCompleteElement);
+            delete this.autoCompleteElement;
+        }
+
+        if (isIntermediateSuggestion)
+            finalSelectionRange.setStart(completionTextNode, wordPrefixLength);
+        else
+            finalSelectionRange.setStart(completionTextNode, completionText.length);
+
+        finalSelectionRange.setEnd(completionTextNode, completionText.length);
+
+        var selection = window.getSelection();
+        selection.removeAllRanges();
+        selection.addRange(finalSelectionRange);
+        if (isIntermediateSuggestion)
+            this.dispatchEventToListeners(WebInspector.TextPrompt.Events.ItemApplied, { itemText: completionText });
+    },
+
+    /**
+     * @param {boolean=} prefixAccepted
+     */
+    acceptSuggestion: function(prefixAccepted)
+    {
+        if (this._isAcceptingSuggestion)
+            return false;
+
+        if (!this.autoCompleteElement || !this.autoCompleteElement.parentNode)
+            return false;
+
+        var text = this.autoCompleteElement.textContent;
+        var textNode = document.createTextNode(text);
+        this.autoCompleteElement.parentNode.replaceChild(textNode, this.autoCompleteElement);
+        delete this.autoCompleteElement;
+
+        var finalSelectionRange = document.createRange();
+        finalSelectionRange.setStart(textNode, text.length);
+        finalSelectionRange.setEnd(textNode, text.length);
+
+        var selection = window.getSelection();
+        selection.removeAllRanges();
+        selection.addRange(finalSelectionRange);
+
+        if (!prefixAccepted) {
+            this.hideSuggestBox();
+            this.dispatchEventToListeners(WebInspector.TextPrompt.Events.ItemAccepted);
+        } else
+            this.autoCompleteSoon(true);
+
+        return true;
+    },
+
+    hideSuggestBox: function()
+    {
+        if (this.isSuggestBoxVisible())
+            this._suggestBox.hide();
+    },
+
+    isSuggestBoxVisible: function()
+    {
+        return this._suggestBox && this._suggestBox.visible();
+    },
+
+    isCaretInsidePrompt: function()
+    {
+        return this._element.isInsertionCaretInside();
+    },
+
+    isCaretAtEndOfPrompt: function()
+    {
+        var selection = window.getSelection();
+        if (!selection.rangeCount || !selection.isCollapsed)
+            return false;
+
+        var selectionRange = selection.getRangeAt(0);
+        var node = selectionRange.startContainer;
+        if (!node.isSelfOrDescendant(this._element))
+            return false;
+
+        if (node.nodeType === Node.TEXT_NODE && selectionRange.startOffset < node.nodeValue.length)
+            return false;
+
+        var foundNextText = false;
+        while (node) {
+            if (node.nodeType === Node.TEXT_NODE && node.nodeValue.length) {
+                if (foundNextText && (!this.autoCompleteElement || !this.autoCompleteElement.isAncestor(node)))
+                    return false;
+                foundNextText = true;
+            }
+
+            node = node.traverseNextNode(this._element);
+        }
+
+        return true;
+    },
+
+    isCaretOnFirstLine: function()
+    {
+        var selection = window.getSelection();
+        var focusNode = selection.focusNode;
+        if (!focusNode || focusNode.nodeType !== Node.TEXT_NODE || focusNode.parentNode !== this._element)
+            return true;
+
+        if (focusNode.textContent.substring(0, selection.focusOffset).indexOf("\n") !== -1)
+            return false;
+        focusNode = focusNode.previousSibling;
+
+        while (focusNode) {
+            if (focusNode.nodeType !== Node.TEXT_NODE)
+                return true;
+            if (focusNode.textContent.indexOf("\n") !== -1)
+                return false;
+            focusNode = focusNode.previousSibling;
+        }
+
+        return true;
+    },
+
+    isCaretOnLastLine: function()
+    {
+        var selection = window.getSelection();
+        var focusNode = selection.focusNode;
+        if (!focusNode || focusNode.nodeType !== Node.TEXT_NODE || focusNode.parentNode !== this._element)
+            return true;
+
+        if (focusNode.textContent.substring(selection.focusOffset).indexOf("\n") !== -1)
+            return false;
+        focusNode = focusNode.nextSibling;
+
+        while (focusNode) {
+            if (focusNode.nodeType !== Node.TEXT_NODE)
+                return true;
+            if (focusNode.textContent.indexOf("\n") !== -1)
+                return false;
+            focusNode = focusNode.nextSibling;
+        }
+
+        return true;
+    },
+
+    moveCaretToEndOfPrompt: function()
+    {
+        var selection = window.getSelection();
+        var selectionRange = document.createRange();
+
+        var offset = this._element.childNodes.length;
+        selectionRange.setStart(this._element, offset);
+        selectionRange.setEnd(this._element, offset);
+
+        selection.removeAllRanges();
+        selection.addRange(selectionRange);
+    },
+
+    tabKeyPressed: function(event)
+    {
+        this._completeCommonPrefix();
+
+        // Consume the key.
+        return true;
+    },
+
+    enterKeyPressed: function(event)
+    {
+        if (this.isSuggestBoxVisible())
+            return this._suggestBox.enterKeyPressed();
+
+        return false;
+    },
+
+    upKeyPressed: function(event)
+    {
+        if (this.isSuggestBoxVisible())
+            return this._suggestBox.upKeyPressed();
+
+        return false;
+    },
+
+    downKeyPressed: function(event)
+    {
+        if (this.isSuggestBoxVisible())
+            return this._suggestBox.downKeyPressed();
+
+        return false;
+    },
+
+    pageUpKeyPressed: function(event)
+    {
+        if (this.isSuggestBoxVisible())
+            return this._suggestBox.pageUpKeyPressed();
+
+        return false;
+    },
+
+    pageDownKeyPressed: function(event)
+    {
+        if (this.isSuggestBoxVisible())
+            return this._suggestBox.pageDownKeyPressed();
+
+        return false;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.TextPrompt}
+ * @param {function(Element, Range, boolean, function(!Array.<string>, number=))} completions
+ * @param {string=} stopCharacters
+ */
+WebInspector.TextPromptWithHistory = function(completions, stopCharacters)
+{
+    WebInspector.TextPrompt.call(this, completions, stopCharacters);
+
+    /**
+     * @type {Array.<string>}
+     */
+    this._data = [];
+
+    /**
+     * 1-based entry in the history stack.
+     * @type {number}
+     */
+    this._historyOffset = 1;
+
+    /**
+     * Whether to coalesce duplicate items in the history, default is true.
+     * @type {boolean}
+     */
+    this._coalesceHistoryDupes = true;
+}
+
+WebInspector.TextPromptWithHistory.prototype = {
+    get historyData()
+    {
+        // FIXME: do we need to copy this?
+        return this._data;
+    },
+
+    setCoalesceHistoryDupes: function(x)
+    {
+        this._coalesceHistoryDupes = x;
+    },
+
+    /**
+     * @param {Array.<string>} data
+     */
+    setHistoryData: function(data)
+    {
+        this._data = [].concat(data);
+        this._historyOffset = 1;
+    },
+
+    /**
+     * Pushes a committed text into the history.
+     * @param {string} text
+     */
+    pushHistoryItem: function(text)
+    {
+        if (this._uncommittedIsTop) {
+            this._data.pop();
+            delete this._uncommittedIsTop;
+        }
+
+        this._historyOffset = 1;
+        if (this._coalesceHistoryDupes && text === this._currentHistoryItem())
+            return;
+        this._data.push(text);
+    },
+
+    /**
+     * Pushes the current (uncommitted) text into the history.
+     */
+    _pushCurrentText: function()
+    {
+        if (this._uncommittedIsTop)
+            this._data.pop(); // Throw away obsolete uncommitted text.
+        this._uncommittedIsTop = true;
+        this.clearAutoComplete(true);
+        this._data.push(this.text);
+    },
+
+    /**
+     * @return {string|undefined}
+     */
+    _previous: function()
+    {
+        if (this._historyOffset > this._data.length)
+            return undefined;
+        if (this._historyOffset === 1)
+            this._pushCurrentText();
+        ++this._historyOffset;
+        return this._currentHistoryItem();
+    },
+
+    /**
+     * @return {string|undefined}
+     */
+    _next: function()
+    {
+        if (this._historyOffset === 1)
+            return undefined;
+        --this._historyOffset;
+        return this._currentHistoryItem();
+    },
+
+    _currentHistoryItem: function()
+    {
+        return this._data[this._data.length - this._historyOffset];
+    },
+
+    /**
+     * @override
+     */
+    defaultKeyHandler: function(event, force)
+    {
+        var newText;
+        var isPrevious;
+
+        switch (event.keyIdentifier) {
+        case "Up":
+            if (!this.isCaretOnFirstLine())
+                break;
+            newText = this._previous();
+            isPrevious = true;
+            break;
+        case "Down":
+            if (!this.isCaretOnLastLine())
+                break;
+            newText = this._next();
+            break;
+        case "U+0050": // Ctrl+P = Previous
+            if (WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) {
+                newText = this._previous();
+                isPrevious = true;
+            }
+            break;
+        case "U+004E": // Ctrl+N = Next
+            if (WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey)
+                newText = this._next();
+            break;
+        }
+
+        if (newText !== undefined) {
+            event.consume(true);
+            this.text = newText;
+
+            if (isPrevious) {
+                var firstNewlineIndex = this.text.indexOf("\n");
+                if (firstNewlineIndex === -1)
+                    this.moveCaretToEndOfPrompt();
+                else {
+                    var selection = window.getSelection();
+                    var selectionRange = document.createRange();
+
+                    selectionRange.setStart(this._element.firstChild, firstNewlineIndex);
+                    selectionRange.setEnd(this._element.firstChild, firstNewlineIndex);
+
+                    selection.removeAllRanges();
+                    selection.addRange(selectionRange);
+                }
+            }
+
+            return true;
+        }
+
+        return WebInspector.TextPrompt.prototype.defaultKeyHandler.apply(this, arguments);
+    },
+
+    __proto__: WebInspector.TextPrompt.prototype
+}
+
diff --git a/Source/devtools/front_end/TextUtils.js b/Source/devtools/front_end/TextUtils.js
new file mode 100644
index 0000000..e603a0c
--- /dev/null
+++ b/Source/devtools/front_end/TextUtils.js
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.TextUtils = {
+    /**
+     * @param {string} char
+     * @return {boolean}
+     */
+    isStopChar: function(char)
+    {
+        return (char > " " && char < "0") ||
+            (char > "9" && char < "A") ||
+            (char > "Z" && char < "_") ||
+            (char > "_" && char < "a") ||
+            (char > "z" && char <= "~");
+    },
+
+    /**
+     * @param {string} char
+     * @return {boolean}
+     */
+    isWordChar: function(char)
+    {
+        return !WebInspector.TextUtils.isStopChar(char) && !WebInspector.TextUtils.isSpaceChar(char);
+    },
+
+    /**
+     * @param {string} char
+     * @return {boolean}
+     */
+    isSpaceChar: function(char)
+    {
+        return WebInspector.TextUtils._SpaceCharRegex.test(char);
+    },
+
+    /**
+     * @param {string} word
+     * @return {boolean}
+     */
+    isWord: function(word)
+    {
+        for (var i = 0; i < word.length; ++i) {
+            if (!WebInspector.TextUtils.isWordChar(word.charAt(i)))
+                return false;
+        }
+        return true;
+    },
+
+    /**
+     * @param {string} char
+     * @return {boolean}
+     */
+    isOpeningBraceChar: function(char)
+    {
+        return char === "(" || char === "{";
+    },
+
+    /**
+     * @param {string} char
+     * @return {boolean}
+     */
+    isClosingBraceChar: function(char)
+    {
+        return char === ")" || char === "}";
+    },
+
+    /**
+     * @param {string} char
+     * @return {boolean}
+     */
+    isBraceChar: function(char)
+    {
+        return WebInspector.TextUtils.isOpeningBraceChar(char) || WebInspector.TextUtils.isClosingBraceChar(char);
+    }
+}
+
+WebInspector.TextUtils._SpaceCharRegex = /\s/;
+
+/**
+ * @enum {string}
+ */
+WebInspector.TextUtils.Indent = {
+    TwoSpaces: "  ",
+    FourSpaces: "    ",
+    EightSpaces: "        ",
+    TabCharacter: "\t"
+}
diff --git a/Source/devtools/front_end/TimelineFrameController.js b/Source/devtools/front_end/TimelineFrameController.js
new file mode 100644
index 0000000..057d4a8
--- /dev/null
+++ b/Source/devtools/front_end/TimelineFrameController.js
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.TimelineModel} model
+ * @param {WebInspector.TimelineOverviewPane} overviewPane
+ * @param {WebInspector.TimelinePresentationModel} presentationModel
+ */
+WebInspector.TimelineFrameController = function(model, overviewPane, presentationModel)
+{
+    this._lastFrame = null;
+    this._model = model;
+    this._overviewPane = overviewPane;
+    this._presentationModel = presentationModel;
+    this._model.addEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onRecordAdded, this);
+    this._model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._onRecordsCleared, this);
+
+    var records = model.records;
+    for (var i = 0; i < records.length; ++i)
+        this._addRecord(records[i]);
+}
+
+WebInspector.TimelineFrameController.prototype = {
+    _onRecordAdded: function(event)
+    {
+        this._addRecord(event.data);
+    },
+
+    _onRecordsCleared: function()
+    {
+        this._lastFrame = null;
+    },
+
+    _addRecord: function(record)
+    {
+        if (record.isBackground)
+            return;
+        var records;
+        var programRecord;
+        if (record.type === WebInspector.TimelineModel.RecordType.Program) {
+            programRecord = record;
+            if (this._lastFrame)
+                this._lastFrame.timeByCategory["other"] += WebInspector.TimelineModel.durationInSeconds(programRecord);
+            records = record["children"] || [];
+        } else
+            records = [record];
+        records.forEach(this._innerAddRecord.bind(this, programRecord));
+    },
+
+    /**
+     * @param {Object} programRecord
+     * @param {Object} record
+     */
+    _innerAddRecord: function(programRecord, record)
+    {
+        var isFrameRecord = record.type === WebInspector.TimelineModel.RecordType.BeginFrame;
+        var programTimeCarryover = isFrameRecord && programRecord ? WebInspector.TimelineModel.endTimeInSeconds(programRecord) - WebInspector.TimelineModel.startTimeInSeconds(record) : 0;
+        if (isFrameRecord && this._lastFrame)
+            this._flushFrame(record, programTimeCarryover);
+        else {
+            if (!this._lastFrame)
+                this._lastFrame = this._createFrame(record, programTimeCarryover);
+            if (!record.thread)
+                WebInspector.TimelineModel.aggregateTimeForRecord(this._lastFrame.timeByCategory, record);
+            var duration = WebInspector.TimelineModel.durationInSeconds(record);
+            this._lastFrame.cpuTime += duration;
+            this._lastFrame.timeByCategory["other"] -= duration;
+        }
+    },
+
+    /**
+     * @param {Object} record
+     * @param {number} programTimeCarryover
+     */
+    _flushFrame: function(record, programTimeCarryover)
+    {
+        this._lastFrame.endTime = WebInspector.TimelineModel.startTimeInSeconds(record);
+        this._lastFrame.duration = this._lastFrame.endTime - this._lastFrame.startTime;
+        this._lastFrame.timeByCategory["other"] -= programTimeCarryover;
+        // Alternatively, we could compute CPU time as sum of all Program events.
+        // This way it's a bit more flexible, as it works in case there's no program events.
+        this._lastFrame.cpuTime += this._lastFrame.timeByCategory["other"];
+        this._overviewPane.addFrame(this._lastFrame);
+        this._presentationModel.addFrame(this._lastFrame);
+        this._lastFrame = this._createFrame(record, programTimeCarryover);
+    },
+
+    /**
+     * @param {Object} record
+     * @param {number} programTimeCarryover
+     */
+    _createFrame: function(record, programTimeCarryover)
+    {
+        var frame = new WebInspector.TimelineFrame();
+        frame.startTime = WebInspector.TimelineModel.startTimeInSeconds(record);
+        frame.startTimeOffset = this._model.recordOffsetInSeconds(record);
+        frame.timeByCategory["other"] = programTimeCarryover;
+        return frame;
+    },
+
+    dispose: function()
+    {
+        this._model.removeEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onRecordAdded, this);
+        this._model.removeEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._onRecordsCleared, this);
+    }
+}
+
+/**
+ * @constructor
+ * @param {Array.<WebInspector.TimelineFrame>} frames
+ */
+WebInspector.FrameStatistics = function(frames)
+{
+    this.frameCount = frames.length;
+    this.minDuration = Infinity;
+    this.maxDuration = 0;
+    this.timeByCategory = {};
+    this.startOffset = frames[0].startTimeOffset;
+    var lastFrame = frames[this.frameCount - 1];
+    this.endOffset = lastFrame.startTimeOffset + lastFrame.duration;
+
+    var totalDuration = 0;
+    var sumOfSquares = 0;
+    for (var i = 0; i < this.frameCount; ++i) {
+        var duration = frames[i].duration;
+        totalDuration += duration;
+        sumOfSquares += duration * duration;
+        this.minDuration = Math.min(this.minDuration, duration);
+        this.maxDuration = Math.max(this.maxDuration, duration);
+        WebInspector.TimelineModel.aggregateTimeByCategory(this.timeByCategory, frames[i].timeByCategory);
+    }
+    this.average = totalDuration / this.frameCount;
+    var variance = sumOfSquares / this.frameCount - this.average * this.average;
+    this.stddev = Math.sqrt(variance);
+}
+
+/**
+ * @constructor
+ */
+WebInspector.TimelineFrame = function()
+{
+    this.timeByCategory = {};
+    this.cpuTime = 0;
+}
diff --git a/Source/devtools/front_end/TimelineGrid.js b/Source/devtools/front_end/TimelineGrid.js
new file mode 100644
index 0000000..7c36751
--- /dev/null
+++ b/Source/devtools/front_end/TimelineGrid.js
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
+ * Copyright (C) 2009 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.TimelineGrid = function()
+{
+    this.element = document.createElement("div");
+
+    this._itemsGraphsElement = document.createElement("div");
+    this._itemsGraphsElement.id = "resources-graphs";
+    this.element.appendChild(this._itemsGraphsElement);
+
+    this._dividersElement = this.element.createChild("div", "resources-dividers");
+
+    this._gridHeaderElement = document.createElement("div"); 
+    this._eventDividersElement = this._gridHeaderElement.createChild("div", "resources-event-dividers");
+    this._dividersLabelBarElement = this._gridHeaderElement.createChild("div", "resources-dividers-label-bar");
+    this.element.appendChild(this._gridHeaderElement);
+
+    this._leftCurtainElement = this.element.createChild("div", "timeline-cpu-curtain-left");
+    this._rightCurtainElement = this.element.createChild("div", "timeline-cpu-curtain-right");
+}
+
+WebInspector.TimelineGrid.prototype = {
+    get itemsGraphsElement()
+    {
+        return this._itemsGraphsElement;
+    },
+
+    get dividersElement()
+    {
+        return this._dividersElement;
+    },
+
+    get dividersLabelBarElement()
+    {
+        return this._dividersLabelBarElement;
+    },
+
+    get gridHeaderElement()
+    {
+        return this._gridHeaderElement;
+    },
+
+    removeDividers: function()
+    {
+        this._dividersElement.removeChildren();
+        this._dividersLabelBarElement.removeChildren();
+    },
+
+    updateDividers: function(calculator)
+    {
+        var dividersElementClientWidth = this._dividersElement.clientWidth;
+        var dividerCount = Math.round(dividersElementClientWidth / 64);
+        var slice = calculator.boundarySpan() / dividerCount;
+
+        this._currentDividerSlice = slice;
+
+        // Reuse divider elements and labels.
+        var divider = this._dividersElement.firstChild;
+        var dividerLabelBar = this._dividersLabelBarElement.firstChild;
+
+        var firstDivider = calculator.paddingLeft > 0 ? 0 : 1;
+        var sliceRemainder = (calculator.minimumBoundary() - calculator.zeroTime()) % slice;
+        for (var i = firstDivider; i <= dividerCount; ++i) {
+            if (!divider) {
+                divider = document.createElement("div");
+                divider.className = "resources-divider";
+                this._dividersElement.appendChild(divider);
+
+                dividerLabelBar = document.createElement("div");
+                dividerLabelBar.className = "resources-divider";
+                var label = document.createElement("div");
+                label.className = "resources-divider-label";
+                dividerLabelBar._labelElement = label;
+                dividerLabelBar.appendChild(label);
+                this._dividersLabelBarElement.appendChild(dividerLabelBar);
+            }
+
+            var left;
+            if (!slice) {
+                left = dividersElementClientWidth / dividerCount * i;
+                dividerLabelBar._labelElement.textContent = "";
+            } else {
+                left = calculator.computePosition(calculator.minimumBoundary() + slice * i - sliceRemainder);
+                dividerLabelBar._labelElement.textContent = calculator.formatTime(slice * i - sliceRemainder);
+            }
+            var percentLeft = 100 * left / dividersElementClientWidth;
+            this._setDividerAndBarLeft(divider, dividerLabelBar, percentLeft);
+
+            divider = divider.nextSibling;
+            dividerLabelBar = dividerLabelBar.nextSibling;
+        }
+
+        // Remove extras.
+        while (divider) {
+            var nextDivider = divider.nextSibling;
+            this._dividersElement.removeChild(divider);
+            divider = nextDivider;
+        }
+        while (dividerLabelBar) {
+            var nextDivider = dividerLabelBar.nextSibling;
+            this._dividersLabelBarElement.removeChild(dividerLabelBar);
+            dividerLabelBar = nextDivider;
+        }
+        return true;
+    },
+
+    _setDividerAndBarLeft: function(divider, dividerLabelBar, percentLeft)
+    {
+        var percentStyleLeft = parseFloat(divider.style.left);
+        if (!isNaN(percentStyleLeft) && Math.abs(percentStyleLeft - percentLeft) < 0.1)
+            return;
+        divider.style.left = percentLeft + "%";
+        dividerLabelBar.style.left = percentLeft + "%";
+    },
+
+    addEventDivider: function(divider)
+    {
+        this._eventDividersElement.appendChild(divider);
+    },
+
+    addEventDividers: function(dividers)
+    {
+        this._gridHeaderElement.removeChild(this._eventDividersElement);
+        for (var i = 0; i < dividers.length; ++i) {
+            if (dividers[i])
+                this._eventDividersElement.appendChild(dividers[i]);
+        }
+        this._gridHeaderElement.appendChild(this._eventDividersElement);
+    },
+
+    removeEventDividers: function()
+    {
+        this._eventDividersElement.removeChildren();
+    },
+
+    hideEventDividers: function()
+    {
+        this._eventDividersElement.addStyleClass("hidden");
+    },
+
+    showEventDividers: function()
+    {
+        this._eventDividersElement.removeStyleClass("hidden");
+    },
+
+    hideCurtains: function()
+    {
+        this._leftCurtainElement.addStyleClass("hidden");
+        this._rightCurtainElement.addStyleClass("hidden");
+    },
+
+    /**
+     * @param {number} gapOffset
+     * @param {number} gapWidth
+     */
+    showCurtains: function(gapOffset, gapWidth)
+    {
+        this._leftCurtainElement.style.width = gapOffset + "px";
+        this._leftCurtainElement.removeStyleClass("hidden");
+        this._rightCurtainElement.style.left = (gapOffset + gapWidth) + "px";
+        this._rightCurtainElement.removeStyleClass("hidden");
+    },
+
+    setScrollAndDividerTop: function(scrollTop, dividersTop)
+    {
+        this._dividersElement.style.top = scrollTop + "px";
+        this._leftCurtainElement.style.top = scrollTop + "px";
+        this._rightCurtainElement.style.top = scrollTop + "px";
+    }
+}
+
+/**
+ * @interface
+ */
+WebInspector.TimelineGrid.Calculator = function() { }
+
+WebInspector.TimelineGrid.Calculator.prototype = {
+    /** @param {number} time */
+    computePosition: function(time) { },
+
+    /** @param {number} time */
+    formatTime: function(time) { },
+
+    /** @return {number} */
+    minimumBoundary: function() { },
+
+    /** @return {number} */
+    zeroTime: function() { },
+
+    /** @return {number} */
+    maximumBoundary: function() { },
+
+    /** @return {number} */
+    boundarySpan: function() { }
+}
diff --git a/Source/devtools/front_end/TimelineManager.js b/Source/devtools/front_end/TimelineManager.js
new file mode 100644
index 0000000..914230e
--- /dev/null
+++ b/Source/devtools/front_end/TimelineManager.js
@@ -0,0 +1,105 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.TimelineManager = function()
+{
+    WebInspector.Object.call(this);
+    this._dispatcher = new WebInspector.TimelineDispatcher(this);
+    this._enablementCount = 0;
+}
+
+WebInspector.TimelineManager.EventTypes = {
+    TimelineStarted: "TimelineStarted",
+    TimelineStopped: "TimelineStopped",
+    TimelineEventRecorded: "TimelineEventRecorded"
+}
+
+WebInspector.TimelineManager.prototype = {
+    /**
+     * @param {number=} maxCallStackDepth
+     * @param {boolean=} includeDomCounters
+     * @param {boolean=} includeNativeMemoryStatistics
+     */
+    start: function(maxCallStackDepth, includeDomCounters, includeNativeMemoryStatistics)
+    {
+        this._enablementCount++;
+        if (this._enablementCount === 1)
+            TimelineAgent.start(maxCallStackDepth, includeDomCounters, includeNativeMemoryStatistics, this._started.bind(this));
+    },
+
+    stop: function()
+    {
+        if (!this._enablementCount) {
+            console.error("WebInspector.TimelineManager start/stop calls are unbalanced");
+            return;
+        }
+        this._enablementCount--;
+        if (!this._enablementCount)
+            TimelineAgent.stop(this._stopped.bind(this));
+    },
+
+    _started: function()
+    {
+        this.dispatchEventToListeners(WebInspector.TimelineManager.EventTypes.TimelineStarted);
+    },
+
+    _stopped: function()
+    {
+        this.dispatchEventToListeners(WebInspector.TimelineManager.EventTypes.TimelineStopped);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {TimelineAgent.Dispatcher}
+ */
+WebInspector.TimelineDispatcher = function(manager)
+{
+    this._manager = manager;
+    InspectorBackend.registerTimelineDispatcher(this);
+}
+
+WebInspector.TimelineDispatcher.prototype = {
+    eventRecorded: function(record)
+    {
+        this._manager.dispatchEventToListeners(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded, record);
+    }
+}
+
+/**
+ * @type {WebInspector.TimelineManager}
+ */
+WebInspector.timelineManager;
diff --git a/Source/devtools/front_end/TimelineModel.js b/Source/devtools/front_end/TimelineModel.js
new file mode 100644
index 0000000..d8315ae
--- /dev/null
+++ b/Source/devtools/front_end/TimelineModel.js
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.TimelineModel = function()
+{
+    this._records = [];
+    this._stringPool = new StringPool();
+    this._minimumRecordTime = -1;
+    this._maximumRecordTime = -1;
+    this._collectionEnabled = false;
+
+    WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded, this._onRecordAdded, this);
+}
+
+WebInspector.TimelineModel.TransferChunkLengthBytes = 5000000;
+
+WebInspector.TimelineModel.RecordType = {
+    Root: "Root",
+    Program: "Program",
+    EventDispatch: "EventDispatch",
+
+    BeginFrame: "BeginFrame",
+    ScheduleStyleRecalculation: "ScheduleStyleRecalculation",
+    RecalculateStyles: "RecalculateStyles",
+    InvalidateLayout: "InvalidateLayout",
+    Layout: "Layout",
+    Paint: "Paint",
+    Rasterize: "Rasterize",
+    ScrollLayer: "ScrollLayer",
+    DecodeImage: "DecodeImage",
+    ResizeImage: "ResizeImage",
+    CompositeLayers: "CompositeLayers",
+
+    ParseHTML: "ParseHTML",
+
+    TimerInstall: "TimerInstall",
+    TimerRemove: "TimerRemove",
+    TimerFire: "TimerFire",
+
+    XHRReadyStateChange: "XHRReadyStateChange",
+    XHRLoad: "XHRLoad",
+    EvaluateScript: "EvaluateScript",
+
+    MarkLoad: "MarkLoad",
+    MarkDOMContent: "MarkDOMContent",
+
+    TimeStamp: "TimeStamp",
+    Time: "Time",
+    TimeEnd: "TimeEnd",
+
+    ScheduleResourceRequest: "ScheduleResourceRequest",
+    ResourceSendRequest: "ResourceSendRequest",
+    ResourceReceiveResponse: "ResourceReceiveResponse",
+    ResourceReceivedData: "ResourceReceivedData",
+    ResourceFinish: "ResourceFinish",
+
+    FunctionCall: "FunctionCall",
+    GCEvent: "GCEvent",
+
+    RequestAnimationFrame: "RequestAnimationFrame",
+    CancelAnimationFrame: "CancelAnimationFrame",
+    FireAnimationFrame: "FireAnimationFrame",
+
+    WebSocketCreate : "WebSocketCreate",
+    WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest",
+    WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse",
+    WebSocketDestroy : "WebSocketDestroy",
+}
+
+WebInspector.TimelineModel.Events = {
+    RecordAdded: "RecordAdded",
+    RecordsCleared: "RecordsCleared"
+}
+
+WebInspector.TimelineModel.startTimeInSeconds = function(record)
+{
+    return record.startTime / 1000;
+}
+
+WebInspector.TimelineModel.endTimeInSeconds = function(record)
+{
+    return (typeof record.endTime === "undefined" ? record.startTime : record.endTime) / 1000;
+}
+
+WebInspector.TimelineModel.durationInSeconds = function(record)
+{
+    return WebInspector.TimelineModel.endTimeInSeconds(record) - WebInspector.TimelineModel.startTimeInSeconds(record);
+}
+
+/**
+ * @param {Object} total
+ * @param {Object} rawRecord
+ */
+WebInspector.TimelineModel.aggregateTimeForRecord = function(total, rawRecord)
+{
+    var childrenTime = 0;
+    var children = rawRecord["children"] || [];
+    for (var i = 0; i < children.length; ++i) {
+        WebInspector.TimelineModel.aggregateTimeForRecord(total, children[i]);
+        childrenTime += WebInspector.TimelineModel.durationInSeconds(children[i]);
+    }
+    var categoryName = WebInspector.TimelinePresentationModel.recordStyle(rawRecord).category.name;
+    var ownTime = WebInspector.TimelineModel.durationInSeconds(rawRecord) - childrenTime;
+    total[categoryName] = (total[categoryName] || 0) + ownTime;
+}
+
+/**
+ * @param {Object} total
+ * @param {Object} addend
+ */
+WebInspector.TimelineModel.aggregateTimeByCategory = function(total, addend)
+{
+    for (var category in addend)
+        total[category] = (total[category] || 0) + addend[category];
+}
+
+WebInspector.TimelineModel.prototype = {
+    /**
+     * @param {boolean=} includeDomCounters
+     * @param {boolean=} includeNativeMemoryStatistics
+     */
+    startRecord: function(includeDomCounters, includeNativeMemoryStatistics)
+    {
+        if (this._collectionEnabled)
+            return;
+        this.reset();
+        var maxStackFrames = WebInspector.settings.timelineLimitStackFramesFlag.get() ? WebInspector.settings.timelineStackFramesToCapture.get() : 30;
+        WebInspector.timelineManager.start(maxStackFrames, includeDomCounters, includeNativeMemoryStatistics);
+        this._collectionEnabled = true;
+    },
+
+    stopRecord: function()
+    {
+        if (!this._collectionEnabled)
+            return;
+        WebInspector.timelineManager.stop();
+        this._collectionEnabled = false;
+    },
+
+    get records()
+    {
+        return this._records;
+    },
+
+    _onRecordAdded: function(event)
+    {
+        if (this._collectionEnabled)
+            this._addRecord(event.data);
+    },
+
+    _addRecord: function(record)
+    {
+        this._stringPool.internObjectStrings(record);
+        this._records.push(record);
+        this._updateBoundaries(record);
+        this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordAdded, record);
+    },
+
+    /**
+     * @param {!Blob} file
+     * @param {!WebInspector.Progress} progress
+     */
+    loadFromFile: function(file, progress)
+    {
+        var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this, progress);
+        var fileReader = this._createFileReader(file, delegate);
+        var loader = new WebInspector.TimelineModelLoader(this, fileReader, progress);
+        fileReader.start(loader);
+    },
+
+    /**
+     * @param {string} url
+     */
+    loadFromURL: function(url, progress)
+    {
+        var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this, progress);
+        var urlReader = new WebInspector.ChunkedXHRReader(url, delegate);
+        var loader = new WebInspector.TimelineModelLoader(this, urlReader, progress);
+        urlReader.start(loader);
+    },
+
+    _createFileReader: function(file, delegate)
+    {
+        return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineModel.TransferChunkLengthBytes, delegate);
+    },
+
+    _createFileWriter: function(fileName, callback)
+    {
+        var stream = new WebInspector.FileOutputStream();
+        stream.open(fileName, callback);
+    },
+
+    saveToFile: function()
+    {
+        var now = new Date();
+        var fileName = "TimelineRawData-" + now.toISO8601Compact() + ".json";
+        function callback(stream)
+        {
+            var saver = new WebInspector.TimelineSaver(stream);
+            saver.save(this._records, window.navigator.appVersion);
+        }
+        this._createFileWriter(fileName, callback.bind(this));
+    },
+
+    reset: function()
+    {
+        this._records = [];
+        this._stringPool.reset();
+        this._minimumRecordTime = -1;
+        this._maximumRecordTime = -1;
+        this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsCleared);
+    },
+
+    minimumRecordTime: function()
+    {
+        return this._minimumRecordTime;
+    },
+
+    maximumRecordTime: function()
+    {
+        return this._maximumRecordTime;
+    },
+
+    _updateBoundaries: function(record)
+    {
+        var startTime = WebInspector.TimelineModel.startTimeInSeconds(record);
+        var endTime = WebInspector.TimelineModel.endTimeInSeconds(record);
+
+        if (this._minimumRecordTime === -1 || startTime < this._minimumRecordTime)
+            this._minimumRecordTime = startTime;
+        if (this._maximumRecordTime === -1 || endTime > this._maximumRecordTime)
+            this._maximumRecordTime = endTime;
+    },
+
+    /**
+     * @param {Object} rawRecord
+     */
+    recordOffsetInSeconds: function(rawRecord)
+    {
+        return WebInspector.TimelineModel.startTimeInSeconds(rawRecord) - this._minimumRecordTime;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.OutputStream}
+ * @param {!WebInspector.TimelineModel} model
+ * @param {!{cancel: function()}} reader
+ * @param {!WebInspector.Progress} progress
+ */
+WebInspector.TimelineModelLoader = function(model, reader, progress)
+{
+    this._model = model;
+    this._reader = reader;
+    this._progress = progress;
+    this._buffer = "";
+    this._firstChunk = true;
+}
+
+WebInspector.TimelineModelLoader.prototype = {
+    /**
+     * @param {string} chunk
+     */
+    write: function(chunk)
+    {
+        var data = this._buffer + chunk;
+        var lastIndex = 0;
+        var index;
+        do {
+            index = lastIndex;
+            lastIndex = WebInspector.findBalancedCurlyBrackets(data, index);
+        } while (lastIndex !== -1)
+
+        var json = data.slice(0, index) + "]";
+        this._buffer = data.slice(index);
+
+        if (!index)
+            return;
+
+        // Prepending "0" to turn string into valid JSON.
+        if (!this._firstChunk)
+            json = "[0" + json;
+
+        var items;
+        try {
+            items = /** @type {Array} */ (JSON.parse(json));
+        } catch (e) {
+            WebInspector.showErrorMessage("Malformed timeline data.");
+            this._model.reset();
+            this._reader.cancel();
+            this._progress.done();
+            return;
+        }
+
+        if (this._firstChunk) {
+            this._version = items[0];
+            this._firstChunk = false;
+            this._model.reset();
+        }
+
+        // Skip 0-th element - it is either version or 0.
+        for (var i = 1, size = items.length; i < size; ++i)
+            this._model._addRecord(items[i]);
+    },
+
+    close: function() { }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.OutputStreamDelegate}
+ * @param {!WebInspector.TimelineModel} model
+ * @param {!WebInspector.Progress} progress
+ */
+WebInspector.TimelineModelLoadFromFileDelegate = function(model, progress)
+{
+    this._model = model;
+    this._progress = progress;
+}
+
+WebInspector.TimelineModelLoadFromFileDelegate.prototype = {
+    onTransferStarted: function()
+    {
+        this._progress.setTitle(WebInspector.UIString("Loading\u2026"));
+    },
+
+    /**
+     * @param {WebInspector.ChunkedReader} reader
+     */
+    onChunkTransferred: function(reader)
+    {
+        if (this._progress.isCanceled()) {
+            reader.cancel();
+            this._progress.done();
+            this._model.reset();
+            return;
+        }
+
+        var totalSize = reader.fileSize();
+        if (totalSize) {
+            this._progress.setTotalWork(totalSize);
+            this._progress.setWorked(reader.loadedSize());
+        }
+    },
+
+    onTransferFinished: function()
+    {
+        this._progress.done();
+    },
+
+    /**
+     * @param {WebInspector.ChunkedReader} reader
+     */
+    onError: function(reader, event)
+    {
+        this._progress.done();
+        this._model.reset();
+        switch (event.target.error.code) {
+        case FileError.NOT_FOUND_ERR:
+            WebInspector.showErrorMessage(WebInspector.UIString("File \"%s\" not found.", reader.fileName()));
+            break;
+        case FileError.NOT_READABLE_ERR:
+            WebInspector.showErrorMessage(WebInspector.UIString("File \"%s\" is not readable", reader.fileName()));
+            break;
+        case FileError.ABORT_ERR:
+            break;
+        default:
+            WebInspector.showErrorMessage(WebInspector.UIString("An error occurred while reading the file \"%s\"", reader.fileName()));
+        }
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.TimelineSaver = function(stream)
+{
+    this._stream = stream;
+}
+
+WebInspector.TimelineSaver.prototype = {
+    /**
+     * @param {Array} records
+     * @param {string} version
+     */
+    save: function(records, version)
+    {
+        this._records = records;
+        this._recordIndex = 0;
+        this._prologue = "[" + JSON.stringify(version);
+
+        this._writeNextChunk(this._stream);
+    },
+
+    _writeNextChunk: function(stream)
+    {
+        const separator = ",\n";
+        var data = [];
+        var length = 0;
+
+        if (this._prologue) {
+            data.push(this._prologue);
+            length += this._prologue.length;
+            delete this._prologue;
+        } else {
+            if (this._recordIndex === this._records.length) {
+                stream.close();
+                return;
+            }
+            data.push("");
+        }
+        while (this._recordIndex < this._records.length) {
+            var item = JSON.stringify(this._records[this._recordIndex]);
+            var itemLength = item.length + separator.length;
+            if (length + itemLength > WebInspector.TimelineModel.TransferChunkLengthBytes)
+                break;
+            length += itemLength;
+            data.push(item);
+            ++this._recordIndex;
+        }
+        if (this._recordIndex === this._records.length)
+            data.push(data.pop() + "]");
+        stream.write(data.join(separator), this._writeNextChunk.bind(this));
+    }
+}
diff --git a/Source/devtools/front_end/TimelineOverviewPane.js b/Source/devtools/front_end/TimelineOverviewPane.js
new file mode 100644
index 0000000..8d6953a
--- /dev/null
+++ b/Source/devtools/front_end/TimelineOverviewPane.js
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.TimelineModel} model
+ */
+WebInspector.TimelineOverviewPane = function(model)
+{
+    WebInspector.View.call(this);
+    this.element.id = "timeline-overview-panel";
+
+    this._windowStartTime = 0;
+    this._windowEndTime = Infinity;
+    this._eventDividers = [];
+
+    this._model = model;
+
+    this._topPaneSidebarElement = document.createElement("div");
+    this._topPaneSidebarElement.id = "timeline-overview-sidebar";
+
+    var overviewTreeElement = document.createElement("ol");
+    overviewTreeElement.className = "sidebar-tree";
+    this._topPaneSidebarElement.appendChild(overviewTreeElement);
+    this.element.appendChild(this._topPaneSidebarElement);
+
+    var topPaneSidebarTree = new TreeOutline(overviewTreeElement);
+
+    this._overviewItems = {};
+    this._overviewItems[WebInspector.TimelineOverviewPane.Mode.Events] = new WebInspector.SidebarTreeElement("timeline-overview-sidebar-events",
+        WebInspector.UIString("Events"));
+    this._overviewItems[WebInspector.TimelineOverviewPane.Mode.Frames] = new WebInspector.SidebarTreeElement("timeline-overview-sidebar-frames",
+        WebInspector.UIString("Frames"));
+    this._overviewItems[WebInspector.TimelineOverviewPane.Mode.Memory] = new WebInspector.SidebarTreeElement("timeline-overview-sidebar-memory",
+        WebInspector.UIString("Memory"));
+
+    for (var mode in this._overviewItems) {
+        var item = this._overviewItems[mode];
+        item.onselect = this.setMode.bind(this, mode);
+        topPaneSidebarTree.appendChild(item);
+    }
+    
+    this._overviewGrid = new WebInspector.OverviewGrid("timeline");
+    this.element.appendChild(this._overviewGrid.element);
+
+    var separatorElement = document.createElement("div");
+    separatorElement.id = "timeline-overview-separator";
+    this.element.appendChild(separatorElement);
+
+    this._innerSetMode(WebInspector.TimelineOverviewPane.Mode.Events);
+
+    var categories = WebInspector.TimelinePresentationModel.categories();
+    for (var category in categories)
+        categories[category].addEventListener(WebInspector.TimelineCategory.Events.VisibilityChanged, this._onCategoryVisibilityChanged, this);
+
+    this._overviewCalculator = new WebInspector.TimelineOverviewCalculator();
+
+    model.addEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onRecordAdded, this);
+    model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._reset, this);
+    this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
+}
+
+WebInspector.TimelineOverviewPane.Mode = {
+    Events: "Events",
+    Frames: "Frames",
+    Memory: "Memory"
+};
+
+WebInspector.TimelineOverviewPane.Events = {
+    ModeChanged: "ModeChanged",
+    WindowChanged: "WindowChanged"
+};
+
+WebInspector.TimelineOverviewPane.prototype = {
+    wasShown: function()
+    {
+        this._update();
+    },
+
+    onResize: function()
+    {
+        this._update();
+    },
+
+    setMode: function(newMode)
+    {
+        if (this._currentMode === newMode)
+            return;
+        this._innerSetMode(newMode);
+        this.dispatchEventToListeners(WebInspector.TimelineOverviewPane.Events.ModeChanged, this._currentMode);
+        this._update();
+    },
+
+    _innerSetMode: function(newMode)
+    {
+        if (this._overviewControl)
+            this._overviewControl.detach();
+
+        this._currentMode = newMode;
+        this._overviewControl = this._createOverviewControl();
+        this._overviewControl.show(this._overviewGrid.element);
+        this._overviewItems[this._currentMode].revealAndSelect(false);
+    },
+
+    /**
+     * @return {WebInspector.TimelineOverviewBase|null}
+     */
+    _createOverviewControl: function()
+    {
+        switch (this._currentMode) {
+        case WebInspector.TimelineOverviewPane.Mode.Events:
+            return new WebInspector.TimelineEventOverview(this._model);
+        case WebInspector.TimelineOverviewPane.Mode.Frames:
+            return new WebInspector.TimelineFrameOverview(this._model);
+        case WebInspector.TimelineOverviewPane.Mode.Memory:
+            return new WebInspector.TimelineMemoryOverview(this._model);
+        }
+        throw new Error("Invalid overview mode: " + this._currentMode);
+    },
+
+    _onCategoryVisibilityChanged: function(event)
+    {
+        this._overviewControl.categoryVisibilityChanged();
+    },
+
+    _update: function()
+    {
+        delete this._refreshTimeout;
+
+        this._updateWindow();
+        this._overviewCalculator.setWindow(this._model.minimumRecordTime(), this._model.maximumRecordTime());
+        this._overviewCalculator.setDisplayWindow(0, this._overviewGrid.clientWidth());
+
+        this._overviewControl.update();
+        this._overviewGrid.updateDividers(this._overviewCalculator);
+        this._updateEventDividers();
+    },
+
+    _updateEventDividers: function()
+    {
+        var records = this._eventDividers;
+        this._overviewGrid.removeEventDividers();
+        var dividers = [];
+        for (var i = 0; i < records.length; ++i) {
+            var record = records[i];
+            var positions = this._overviewCalculator.computeBarGraphPercentages(record);
+            var dividerPosition = Math.round(positions.start * 10);
+            if (dividers[dividerPosition])
+                continue;
+            var divider = WebInspector.TimelinePresentationModel.createEventDivider(record.type);
+            divider.style.left = positions.start + "%";
+            dividers[dividerPosition] = divider;
+        }
+        this._overviewGrid.addEventDividers(dividers);
+    },
+
+    /**
+     * @param {number} width
+     */
+    sidebarResized: function(width)
+    {
+        this._overviewGrid.element.style.left = width + "px";
+        this._topPaneSidebarElement.style.width = width + "px";
+        this._update();
+    },
+
+    /**
+     * @param {WebInspector.TimelineFrame} frame
+     */
+    addFrame: function(frame)
+    {
+        this._overviewControl.addFrame(frame);
+        this._scheduleRefresh();
+    },
+
+    /**
+     * @param {WebInspector.TimelineFrame} frame
+     */
+    zoomToFrame: function(frame)
+    {
+        var frameOverview = /** @type WebInspector.TimelineFrameOverview */ (this._overviewControl);
+        var window = frameOverview.framePosition(frame);
+        if (!window)
+            return;
+
+        this._overviewGrid.setWindowPosition(window.start, window.end);
+    },
+
+    _onRecordAdded: function(event)
+    {
+        var record = event.data;
+        var eventDividers = this._eventDividers;
+        function addEventDividers(record)
+        {
+            if (WebInspector.TimelinePresentationModel.isEventDivider(record))
+                eventDividers.push(record);
+        }
+        WebInspector.TimelinePresentationModel.forAllRecords([record], addEventDividers);
+        this._scheduleRefresh();
+    },
+
+    _reset: function()
+    {
+        this._windowStartTime = 0;
+        this._windowEndTime = Infinity;
+        this._overviewCalculator.reset();
+        this._overviewGrid.reset();
+        this._eventDividers = [];
+        this._overviewGrid.updateDividers(this._overviewCalculator);
+        this._overviewControl.reset();
+        this._update();
+    },
+
+    windowStartTime: function()
+    {
+        return this._windowStartTime || this._model.minimumRecordTime();
+    },
+
+    windowEndTime: function()
+    {
+        return this._windowEndTime < Infinity ? this._windowEndTime : this._model.maximumRecordTime();
+    },
+
+    windowLeft: function()
+    {
+        return this._overviewGrid.windowLeft();
+    },
+
+    windowRight: function()
+    {
+        return this._overviewGrid.windowRight();
+    },
+
+    _onWindowChanged: function()
+    {
+        if (this._ignoreWindowChangedEvent)
+            return;
+        var times = this._overviewControl.windowTimes(this.windowLeft(), this.windowRight());
+        this._windowStartTime = times.startTime;
+        this._windowEndTime = times.endTime;
+        this.dispatchEventToListeners(WebInspector.TimelineOverviewPane.Events.WindowChanged);
+    },
+
+    /**
+     * @param {Number} left
+     * @param {Number} right
+     */
+    setWindowTimes: function(left, right)
+    {
+        this._windowStartTime = left;
+        this._windowEndTime = right;
+        this._updateWindow();
+    },
+
+    _updateWindow: function()
+    {
+        var offset = this._model.minimumRecordTime();
+        var timeSpan = this._model.maximumRecordTime() - offset;
+        var left = this._windowStartTime ? (this._windowStartTime - offset) / timeSpan : 0;
+        var right = this._windowEndTime < Infinity ? (this._windowEndTime - offset) / timeSpan : 1;
+        this._ignoreWindowChangedEvent = true;
+        this._overviewGrid.setWindow(left, right);
+        this._ignoreWindowChangedEvent = false;
+    },
+
+    _scheduleRefresh: function()
+    {
+        if (this._refreshTimeout)
+            return;
+        if (!this.isShowing())
+            return;
+        this._refreshTimeout = setTimeout(this._update.bind(this), 300);
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelineGrid.Calculator}
+ */
+WebInspector.TimelineOverviewCalculator = function()
+{
+}
+
+WebInspector.TimelineOverviewCalculator.prototype = {
+    /**
+     * @param {number} time
+     */
+    computePosition: function(time)
+    {
+        return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea + this.paddingLeft;
+    },
+
+    computeBarGraphPercentages: function(record)
+    {
+        var start = (WebInspector.TimelineModel.startTimeInSeconds(record) - this._minimumBoundary) / this.boundarySpan() * 100;
+        var end = (WebInspector.TimelineModel.endTimeInSeconds(record) - this._minimumBoundary) / this.boundarySpan() * 100;
+        return {start: start, end: end};
+    },
+
+    /**
+     * @param {number=} minimum
+     * @param {number=} maximum
+     */
+    setWindow: function(minimum, maximum)
+    {
+        this._minimumBoundary = minimum >= 0 ? minimum : undefined;
+        this._maximumBoundary = maximum >= 0 ? maximum : undefined;
+    },
+
+    /**
+     * @param {number} paddingLeft
+     * @param {number} clientWidth
+     */
+    setDisplayWindow: function(paddingLeft, clientWidth)
+    {
+        this._workingArea = clientWidth - paddingLeft;
+        this.paddingLeft = paddingLeft;
+    },
+
+    reset: function()
+    {
+        this.setWindow();
+    },
+
+    formatTime: function(value)
+    {
+        return Number.secondsToString(value);
+    },
+
+    maximumBoundary: function()
+    {
+        return this._maximumBoundary;
+    },
+
+    minimumBoundary: function()
+    {
+        return this._minimumBoundary;
+    },
+
+    zeroTime: function()
+    {
+        return this._minimumBoundary;
+    },
+
+    boundarySpan: function()
+    {
+        return this._maximumBoundary - this._minimumBoundary;
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.View}
+ * @param {WebInspector.TimelineModel} model
+ */
+WebInspector.TimelineOverviewBase = function(model)
+{
+    WebInspector.View.call(this);
+    this.element.classList.add("fill");
+
+    this._model = model;
+    this._canvas = this.element.createChild("canvas", "fill");
+    this._context = this._canvas.getContext("2d");
+}
+
+WebInspector.TimelineOverviewBase.prototype = {
+    update: function() { },
+    reset: function() { },
+
+    categoryVisibilityChanged: function() { },
+
+    /**
+     * @param {WebInspector.TimelineFrame} frame
+     */
+    addFrame: function(frame) { },
+
+    /**
+     * @param {number} windowLeft
+     * @param {number} windowRight
+     */
+    windowTimes: function(windowLeft, windowRight)
+    {
+        var absoluteMin = this._model.minimumRecordTime();
+        var absoluteMax = this._model.maximumRecordTime();
+        return {
+            startTime: absoluteMin + (absoluteMax - absoluteMin) * windowLeft,
+            endTime: absoluteMin + (absoluteMax - absoluteMin) * windowRight
+        };
+    },
+
+    _resetCanvas: function()
+    {
+        this._canvas.width = this.element.clientWidth * window.devicePixelRatio;
+        this._canvas.height = this.element.clientHeight * window.devicePixelRatio;
+    },
+
+    __proto__: WebInspector.View.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TimelineOverviewBase}
+ * @param {WebInspector.TimelineModel} model
+ */
+WebInspector.TimelineMemoryOverview = function(model)
+{
+    WebInspector.TimelineOverviewBase.call(this, model);
+    this.element.id = "timeline-overview-memory";
+
+    this._maxHeapSizeLabel = this.element.createChild("div", "max memory-graph-label");
+    this._minHeapSizeLabel = this.element.createChild("div", "min memory-graph-label");
+}
+
+WebInspector.TimelineMemoryOverview.prototype = {
+    update: function()
+    {
+        var records = this._model.records;
+        if (!records.length)
+            return;
+
+        this._resetCanvas();
+
+        const lowerOffset = 3;
+        var maxUsedHeapSize = 0;
+        var minUsedHeapSize = 100000000000;
+        var minTime = this._model.minimumRecordTime();
+        var maxTime = this._model.maximumRecordTime();;
+        WebInspector.TimelinePresentationModel.forAllRecords(records, function(r) {
+            maxUsedHeapSize = Math.max(maxUsedHeapSize, r.usedHeapSize || maxUsedHeapSize);
+            minUsedHeapSize = Math.min(minUsedHeapSize, r.usedHeapSize || minUsedHeapSize);
+        });
+        minUsedHeapSize = Math.min(minUsedHeapSize, maxUsedHeapSize);
+
+        var width = this._canvas.width;
+        var height = this._canvas.height - lowerOffset;
+        var xFactor = width / (maxTime - minTime);
+        var yFactor = height / (maxUsedHeapSize - minUsedHeapSize);
+
+        var histogram = new Array(width);
+        WebInspector.TimelinePresentationModel.forAllRecords(records, function(r) {
+            if (!r.usedHeapSize)
+                return;
+            var x = Math.round((WebInspector.TimelineModel.endTimeInSeconds(r) - minTime) * xFactor);
+            var y = Math.round((r.usedHeapSize - minUsedHeapSize) * yFactor);
+            histogram[x] = Math.max(histogram[x] || 0, y);
+        });
+
+        var ctx = this._context;
+        this._clear(ctx);
+
+        // +1 so that the border always fit into the canvas area.
+        height = height + 1;
+
+        ctx.beginPath();
+        var initialY = 0;
+        for (var k = 0; k < histogram.length; k++) {
+            if (histogram[k]) {
+                initialY = histogram[k];
+                break;
+            }
+        }
+        ctx.moveTo(0, height - initialY);
+
+        for (var x = 0; x < histogram.length; x++) {
+             if (!histogram[x])
+                 continue;
+             ctx.lineTo(x, height - histogram[x]);
+        }
+
+        ctx.lineWidth = 0.5;
+        ctx.strokeStyle = "rgba(20,0,0,0.8)";
+        ctx.stroke();
+
+        ctx.fillStyle = "rgba(214,225,254, 0.8);";
+        ctx.lineTo(width, this._canvas.height);
+        ctx.lineTo(0, this._canvas.height);
+        ctx.lineTo(0, height - initialY);
+        ctx.fill();
+        ctx.closePath();
+
+        this._maxHeapSizeLabel.textContent = Number.bytesToString(maxUsedHeapSize);
+        this._minHeapSizeLabel.textContent = Number.bytesToString(minUsedHeapSize);
+    },
+
+    _clear: function(ctx)
+    {
+        ctx.fillStyle = "rgba(255,255,255,0.8)";
+        ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
+    },
+
+    __proto__: WebInspector.TimelineOverviewBase.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TimelineOverviewBase}
+ * @param {WebInspector.TimelineModel} model
+ */
+WebInspector.TimelineEventOverview = function(model)
+{
+    WebInspector.TimelineOverviewBase.call(this, model);
+
+    this.element.id = "timeline-overview-events";
+
+    this._fillStyles = {};
+    var categories = WebInspector.TimelinePresentationModel.categories();
+    for (var category in categories)
+        this._fillStyles[category] = WebInspector.TimelinePresentationModel.createFillStyleForCategory(this._context, 0, WebInspector.TimelineEventOverview._stripGradientHeight, categories[category]);
+
+    this._disabledCategoryFillStyle = WebInspector.TimelinePresentationModel.createFillStyle(this._context, 0, WebInspector.TimelineEventOverview._stripGradientHeight,
+        "rgb(218, 218, 218)", "rgb(170, 170, 170)", "rgb(143, 143, 143)");
+
+    this._disabledCategoryBorderStyle = "rgb(143, 143, 143)";
+}
+
+/** @const */
+WebInspector.TimelineEventOverview._numberOfStrips = 3;
+
+/** @const */
+WebInspector.TimelineEventOverview._stripGradientHeight = 120;
+
+WebInspector.TimelineEventOverview.prototype = {
+    update: function()
+    {
+        this._resetCanvas();
+
+        var stripHeight = Math.round(this._canvas.height  / WebInspector.TimelineEventOverview._numberOfStrips);
+        var timeOffset = this._model.minimumRecordTime();
+        var timeSpan = this._model.maximumRecordTime() - timeOffset;
+        var scale = this._canvas.width / timeSpan;
+
+        var lastBarByGroup = [];
+
+        this._context.fillStyle = "rgba(0, 0, 0, 0.05)";
+        for (var i = 1; i < WebInspector.TimelineEventOverview._numberOfStrips; i += 2)
+            this._context.fillRect(0.5, i * stripHeight + 0.5, this._canvas.width, stripHeight);
+
+        function appendRecord(record)
+        {
+            if (record.type === WebInspector.TimelineModel.RecordType.BeginFrame)
+                return;
+            var recordStart = Math.floor((WebInspector.TimelineModel.startTimeInSeconds(record) - timeOffset) * scale);
+            var recordEnd = Math.ceil((WebInspector.TimelineModel.endTimeInSeconds(record) - timeOffset) * scale);
+            var category = WebInspector.TimelinePresentationModel.categoryForRecord(record);
+            if (category.overviewStripGroupIndex < 0)
+                return;
+            var bar = lastBarByGroup[category.overviewStripGroupIndex];
+            // This bar may be merged with previous -- so just adjust the previous bar.
+            const barsMergeThreshold = 2;
+            if (bar && bar.category === category && bar.end + barsMergeThreshold >= recordStart) {
+                if (recordEnd > bar.end)
+                    bar.end = recordEnd;
+                return;
+            }
+            if (bar)
+                this._renderBar(bar.start, bar.end, stripHeight, bar.category);
+            lastBarByGroup[category.overviewStripGroupIndex] = { start: recordStart, end: recordEnd, category: category };
+        }
+        WebInspector.TimelinePresentationModel.forAllRecords(this._model.records, appendRecord.bind(this));
+        for (var i = 0; i < lastBarByGroup.length; ++i) {
+            if (lastBarByGroup[i])
+                this._renderBar(lastBarByGroup[i].start, lastBarByGroup[i].end, stripHeight, lastBarByGroup[i].category);
+        }
+    },
+
+    categoryVisibilityChanged: function()
+    {
+        this.update();
+    },
+
+    /**
+     * @param {number} begin
+     * @param {number} end
+     * @param {number} height
+     * @param {WebInspector.TimelineCategory} category
+     */
+    _renderBar: function(begin, end, height, category)
+    {
+        const stripPadding = 4 * window.devicePixelRatio;
+        const innerStripHeight = height - 2 * stripPadding;
+
+        var x = begin + 0.5;
+        var y = category.overviewStripGroupIndex * height + stripPadding + 0.5;
+        var width = Math.max(end - begin, 1);
+
+        this._context.save();
+        this._context.translate(x, y);
+        this._context.scale(1, innerStripHeight / WebInspector.TimelineEventOverview._stripGradientHeight);
+        this._context.fillStyle = category.hidden ? this._disabledCategoryFillStyle : this._fillStyles[category.name];
+        this._context.fillRect(0, 0, width, WebInspector.TimelineEventOverview._stripGradientHeight);
+        this._context.strokeStyle = category.hidden ? this._disabledCategoryBorderStyle : category.borderColor;
+        this._context.strokeRect(0, 0, width, WebInspector.TimelineEventOverview._stripGradientHeight);
+        this._context.restore();
+    },
+
+    __proto__: WebInspector.TimelineOverviewBase.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.TimelineOverviewBase}
+ * @param {WebInspector.TimelineModel} model
+ */
+WebInspector.TimelineFrameOverview = function(model)
+{
+    WebInspector.TimelineOverviewBase.call(this, model);
+    this.element.id = "timeline-overview-frames";
+    this.reset();
+
+    this._outerPadding = 4 * window.devicePixelRatio;
+    this._maxInnerBarWidth = 10 * window.devicePixelRatio;
+
+    // The below two are really computed by update() -- but let's have something so that windowTimes() is happy.
+    this._actualPadding = 5 * window.devicePixelRatio;
+    this._actualOuterBarWidth = this._maxInnerBarWidth + this._actualPadding;
+
+    this._fillStyles = {};
+    var categories = WebInspector.TimelinePresentationModel.categories();
+    for (var category in categories)
+        this._fillStyles[category] = WebInspector.TimelinePresentationModel.createFillStyleForCategory(this._context, this._maxInnerBarWidth, 0, categories[category]);
+}
+
+WebInspector.TimelineFrameOverview.prototype = {
+    reset: function()
+    {
+        this._recordsPerBar = 1;
+        this._barTimes = [];
+        this._frames = [];
+    },
+
+    update: function()
+    {
+        const minBarWidth = 4 * window.devicePixelRatio;
+        this._resetCanvas();
+        this._framesPerBar = Math.max(1, this._frames.length * minBarWidth / this._canvas.width);
+        this._barTimes = [];
+        var visibleFrames = this._aggregateFrames(this._framesPerBar);
+
+        const paddingTop = 4 * window.devicePixelRatio;
+
+        // Optimize appearance for 30fps. However, if at least half frames won't fit at this scale,
+        // fall back to using autoscale.
+        const targetFPS = 30;
+        var fullBarLength = 1.0 / targetFPS;
+        if (fullBarLength < this._medianFrameLength)
+            fullBarLength = Math.min(this._medianFrameLength * 2, this._maxFrameLength);
+
+        var scale = (this._canvas.height - paddingTop) / fullBarLength;
+        this._renderBars(visibleFrames, scale);
+    },
+
+    /**
+     * @param {WebInspector.TimelineFrame} frame
+     */
+    addFrame: function(frame)
+    {
+        this._frames.push(frame);
+    },
+
+    framePosition: function(frame)
+    {
+        var frameNumber = this._frames.indexOf(frame);
+        if (frameNumber < 0)
+            return;
+        var barNumber = Math.floor(frameNumber / this._framesPerBar);
+        var firstBar = this._framesPerBar > 1 ? barNumber : Math.max(barNumber - 1, 0);
+        var lastBar = this._framesPerBar > 1 ? barNumber : Math.min(barNumber + 1, this._barTimes.length - 1);
+        return {
+            start: Math.ceil(this._barNumberToScreenPosition(firstBar) - this._actualPadding / 2),
+            end: Math.floor(this._barNumberToScreenPosition(lastBar + 1) - this._actualPadding / 2)
+        }
+    },
+
+    /**
+     * @param {number} framesPerBar
+     */
+    _aggregateFrames: function(framesPerBar)
+    {
+        var visibleFrames = [];
+        var durations = [];
+
+        this._maxFrameLength = 0;
+
+        for (var barNumber = 0, currentFrame = 0; currentFrame < this._frames.length; ++barNumber) {
+            var barStartTime = this._frames[currentFrame].startTime;
+            var longestFrame = null;
+
+            for (var lastFrame = Math.min(Math.floor((barNumber + 1) * framesPerBar), this._frames.length);
+                 currentFrame < lastFrame; ++currentFrame) {
+                if (!longestFrame || longestFrame.duration < this._frames[currentFrame].duration)
+                    longestFrame = this._frames[currentFrame];
+            }
+            var barEndTime = this._frames[currentFrame - 1].endTime;
+            if (longestFrame) {
+                this._maxFrameLength = Math.max(this._maxFrameLength, longestFrame.duration);
+                visibleFrames.push(longestFrame);
+                this._barTimes.push({ startTime: barStartTime, endTime: barEndTime });
+                durations.push(longestFrame.duration);
+            }
+        }
+        this._medianFrameLength = durations.qselect(Math.floor(durations.length / 2));
+        return visibleFrames;
+    },
+
+    /**
+     * @param {Array.<WebInspector.TimelineFrame>} frames
+     * @param {number} scale
+     */
+    _renderBars: function(frames, scale)
+    {
+        const maxPadding = 5 * window.devicePixelRatio;
+        this._actualOuterBarWidth = Math.min((this._canvas.width - 2 * this._outerPadding) / frames.length, this._maxInnerBarWidth + maxPadding);
+        this._actualPadding = Math.min(Math.floor(this._actualOuterBarWidth / 3), maxPadding);
+
+        var barWidth = this._actualOuterBarWidth - this._actualPadding;
+        for (var i = 0; i < frames.length; ++i)
+            this._renderBar(this._barNumberToScreenPosition(i), barWidth, frames[i], scale);
+
+        this._drawFPSMarks(scale);
+    },
+
+    /**
+     * @param {number} n
+     */
+    _barNumberToScreenPosition: function(n)
+    {
+        return this._outerPadding + this._actualOuterBarWidth * n;
+    },
+
+    /**
+     * @param {number} scale
+     */
+    _drawFPSMarks: function(scale)
+    {
+        const fpsMarks = [30, 60];
+
+        this._context.save();
+        this._context.beginPath();
+        this._context.font = 9 * window.devicePixelRatio + "px monospace";
+        this._context.textAlign = "right";
+        this._context.textBaseline = "top";
+
+        const labelPadding = 2 * window.devicePixelRatio;
+        var lineHeight = 12 * window.devicePixelRatio;
+        var labelTopMargin = 0;
+
+        for (var i = 0; i < fpsMarks.length; ++i) {
+            var fps = fpsMarks[i];
+            // Draw lines one pixel above they need to be, so 60pfs line does not cross most of the frames tops.
+            var y = this._canvas.height - Math.floor(1.0 / fps * scale) - 0.5;
+            var label = fps + " FPS ";
+            var labelWidth = this._context.measureText(label).width;
+            var labelX = this._canvas.width;
+            var labelY;
+
+            if (labelTopMargin < y - lineHeight)
+                labelY = y - lineHeight;
+            else if (y + lineHeight < this._canvas.height)
+                labelY = y;
+            else
+                break; // No space for the label, so no line as well.
+
+            this._context.moveTo(0, y);
+            this._context.lineTo(this._canvas.width, y);
+
+            this._context.fillStyle = "rgba(255, 255, 255, 0.75)";
+            this._context.fillRect(labelX - labelWidth - labelPadding, labelY, labelWidth + 2 * labelPadding, lineHeight);
+            this._context.fillStyle = "rgb(0, 0, 0)";
+            this._context.fillText(label, labelX, labelY);
+            labelTopMargin = labelY + lineHeight;
+        }
+        this._context.strokeStyle = "rgb(0, 0, 0, 0.3)";
+        this._context.stroke();
+        this._context.restore();
+    },
+
+    _renderBar: function(left, width, frame, scale)
+    {
+        var categories = Object.keys(WebInspector.TimelinePresentationModel.categories());
+        if (!categories.length)
+            return;
+        var x = Math.floor(left) + 0.5;
+        width = Math.floor(width);
+
+        for (var i = 0, bottomOffset = this._canvas.height; i < categories.length; ++i) {
+            var category = categories[i];
+            var duration = frame.timeByCategory[category];
+
+            if (!duration)
+                continue;
+            var height = duration * scale;
+            var y = Math.floor(bottomOffset - height) + 0.5;
+
+            this._context.save();
+            this._context.translate(x, 0);
+            this._context.scale(width / this._maxInnerBarWidth, 1);
+            this._context.fillStyle = this._fillStyles[category];
+            this._context.fillRect(0, y, this._maxInnerBarWidth, Math.floor(height));
+            this._context.strokeStyle = WebInspector.TimelinePresentationModel.categories()[category].borderColor;
+            this._context.beginPath();
+            this._context.moveTo(0, y);
+            this._context.lineTo(this._maxInnerBarWidth, y);
+            this._context.stroke();
+            this._context.restore();
+
+            bottomOffset -= height - 1;
+        }
+        // Draw a contour for the total frame time.
+        var y0 = Math.floor(this._canvas.height - frame.duration * scale) + 0.5;
+        var y1 = this._canvas.height + 0.5;
+
+        this._context.strokeStyle = "rgba(90, 90, 90, 0.3)";
+        this._context.beginPath();
+        this._context.moveTo(x, y1);
+        this._context.lineTo(x, y0);
+        this._context.lineTo(x + width, y0);
+        this._context.lineTo(x + width, y1);
+        this._context.stroke();
+    },
+
+    windowTimes: function(windowLeft, windowRight)
+    {
+        var windowSpan = this.element.clientWidth;
+        var leftOffset = windowLeft * windowSpan - this._outerPadding + this._actualPadding;
+        var rightOffset = windowRight * windowSpan - this._outerPadding;
+        var bars = this.element.children;
+        var firstBar = Math.floor(Math.max(leftOffset, 0) / this._actualOuterBarWidth);
+        var lastBar = Math.min(Math.floor(rightOffset / this._actualOuterBarWidth), this._barTimes.length - 1);
+        const snapToRightTolerancePixels = 3;
+        return {
+            startTime: firstBar >= this._barTimes.length ? Infinity : this._barTimes[firstBar].startTime,
+            endTime: rightOffset + snapToRightTolerancePixels > windowSpan ? Infinity : this._barTimes[lastBar].endTime
+        }
+    },
+
+    __proto__: WebInspector.TimelineOverviewBase.prototype
+}
+
+/**
+ * @param {WebInspector.TimelineOverviewPane} pane
+ * @constructor
+ * @implements {WebInspector.TimelinePresentationModel.Filter}
+ */
+WebInspector.TimelineWindowFilter = function(pane)
+{
+    this._pane = pane;
+}
+
+WebInspector.TimelineWindowFilter.prototype = {
+    /**
+     * @param {!WebInspector.TimelinePresentationModel.Record} record
+     * @return {boolean}
+     */
+    accept: function(record)
+    {
+        return record.lastChildEndTime >= this._pane._windowStartTime && record.startTime <= this._pane._windowEndTime;
+    }
+}
diff --git a/Source/devtools/front_end/TimelinePanel.js b/Source/devtools/front_end/TimelinePanel.js
new file mode 100644
index 0000000..043a9e2
--- /dev/null
+++ b/Source/devtools/front_end/TimelinePanel.js
@@ -0,0 +1,1619 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2012 Intel 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.
+ */
+
+importScript("MemoryStatistics.js");
+importScript("DOMCountersGraph.js");
+importScript("NativeMemoryGraph.js");
+importScript("TimelineModel.js");
+importScript("TimelineOverviewPane.js");
+importScript("TimelinePresentationModel.js");
+importScript("TimelineFrameController.js");
+
+/**
+ * @constructor
+ * @extends {WebInspector.Panel}
+ */
+WebInspector.TimelinePanel = function()
+{
+    WebInspector.Panel.call(this, "timeline");
+    this.registerRequiredCSS("timelinePanel.css");
+
+    this._model = new WebInspector.TimelineModel();
+    this._presentationModel = new WebInspector.TimelinePresentationModel();
+
+    this._overviewModeSetting = WebInspector.settings.createSetting("timelineOverviewMode", WebInspector.TimelineOverviewPane.Mode.Events);
+    this._glueRecordsSetting = WebInspector.settings.createSetting("timelineGlueRecords", false);
+
+    this._overviewPane = new WebInspector.TimelineOverviewPane(this._model);
+    this._overviewPane.addEventListener(WebInspector.TimelineOverviewPane.Events.WindowChanged, this._invalidateAndScheduleRefresh.bind(this, false, true));
+    this._overviewPane.addEventListener(WebInspector.TimelineOverviewPane.Events.ModeChanged, this._overviewModeChanged, this);
+    this._overviewPane.show(this.element);
+
+    this.element.addEventListener("contextmenu", this._contextMenu.bind(this), false);
+
+    this.element.addStyleClass("split-view-vertical");
+
+    this._sidebarBackgroundElement = document.createElement("div");
+    this._sidebarBackgroundElement.className = "sidebar split-view-sidebar split-view-contents-first timeline-sidebar-background";
+    this.element.appendChild(this._sidebarBackgroundElement);
+
+    this.createSidebarViewWithTree();
+    this.element.appendChild(this.splitView.resizerElement());
+
+    this._containerElement = this.splitView.element;
+    this._containerElement.tabIndex = 0;
+    this._containerElement.id = "timeline-container";
+    this._containerElement.addEventListener("scroll", this._onScroll.bind(this), false);
+
+    this._timelineMemorySplitter = this.element.createChild("div");
+    this._timelineMemorySplitter.id = "timeline-memory-splitter";
+    WebInspector.installDragHandle(this._timelineMemorySplitter, this._startSplitterDragging.bind(this), this._splitterDragging.bind(this), this._endSplitterDragging.bind(this), "ns-resize");
+    this._timelineMemorySplitter.addStyleClass("hidden");
+    this._includeDomCounters = false;
+    this._includeNativeMemoryStatistics = false;
+    if (WebInspector.experimentsSettings.nativeMemoryTimeline.isEnabled()) {
+        this._memoryStatistics = new WebInspector.NativeMemoryGraph(this, this._model, this.splitView.sidebarWidth());
+        this._includeNativeMemoryStatistics = true;
+    } else {
+        this._memoryStatistics = new WebInspector.DOMCountersGraph(this, this._model, this.splitView.sidebarWidth());
+        this._includeDomCounters = true;
+    }
+    WebInspector.settings.memoryCounterGraphsHeight = WebInspector.settings.createSetting("memoryCounterGraphsHeight", 150);
+
+    var itemsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RECORDS"), {}, true);
+    this.sidebarTree.appendChild(itemsTreeElement);
+    this.sidebarTree.setFocusable(false);
+
+    this._sidebarListElement = document.createElement("div");
+    this.sidebarElement.appendChild(this._sidebarListElement);
+
+    this._containerContentElement = this.splitView.mainElement;
+    this._containerContentElement.id = "resources-container-content";
+
+    this._timelineGrid = new WebInspector.TimelineGrid();
+    this._itemsGraphsElement = this._timelineGrid.itemsGraphsElement;
+    this._itemsGraphsElement.id = "timeline-graphs";
+    this._containerContentElement.appendChild(this._timelineGrid.element);
+    this._timelineGrid.gridHeaderElement.id = "timeline-grid-header";
+    this._memoryStatistics.setMainTimelineGrid(this._timelineGrid);
+    this.element.appendChild(this._timelineGrid.gridHeaderElement);
+
+    this._topGapElement = document.createElement("div");
+    this._topGapElement.className = "timeline-gap";
+    this._itemsGraphsElement.appendChild(this._topGapElement);
+
+    this._graphRowsElement = document.createElement("div");
+    this._itemsGraphsElement.appendChild(this._graphRowsElement);
+
+    this._bottomGapElement = document.createElement("div");
+    this._bottomGapElement.className = "timeline-gap";
+    this._itemsGraphsElement.appendChild(this._bottomGapElement);
+
+    this._expandElements = document.createElement("div");
+    this._expandElements.id = "orphan-expand-elements";
+    this._itemsGraphsElement.appendChild(this._expandElements);
+
+    this._calculator = new WebInspector.TimelineCalculator(this._model);
+    this._createStatusBarItems();
+
+    this._frameMode = false;
+    this._boundariesAreValid = true;
+    this._scrollTop = 0;
+
+    this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this));
+    this.element.addEventListener("mousemove", this._mouseMove.bind(this), false);
+    this.element.addEventListener("mouseout", this._mouseOut.bind(this), false);
+
+    // Short events filter is disabled by default.
+    this._durationFilter = new WebInspector.TimelineIsLongFilter();
+
+    this._expandOffset = 15;
+
+    this._headerLineCount = 1;
+    this._adjustHeaderHeight();
+
+    this._mainThreadTasks = /** @type {!Array.<{startTime: number, endTime: number}>} */ ([]);
+    this._cpuBarsElement = this._timelineGrid.gridHeaderElement.createChild("div", "timeline-cpu-bars");
+    this._mainThreadMonitoringEnabled = WebInspector.settings.showCpuOnTimelineRuler.get();
+    WebInspector.settings.showCpuOnTimelineRuler.addChangeListener(this._showCpuOnTimelineRulerChanged, this);
+
+    this._createFileSelector();
+
+    this._model.addEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onTimelineEventRecorded, this);
+    this._model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._onRecordsCleared, this);
+
+    this._registerShortcuts();
+
+    this._allRecordsCount = 0;
+
+    this._presentationModel.addFilter(new WebInspector.TimelineWindowFilter(this._overviewPane));
+    this._presentationModel.addFilter(new WebInspector.TimelineCategoryFilter()); 
+    this._presentationModel.addFilter(this._durationFilter);
+}
+
+// Define row height, should be in sync with styles for timeline graphs.
+WebInspector.TimelinePanel.rowHeight = 18;
+
+WebInspector.TimelinePanel.durationFilterPresetsMs = [0, 1, 15];
+
+WebInspector.TimelinePanel.prototype = {
+    _showCpuOnTimelineRulerChanged: function()
+    {
+        var mainThreadMonitoringEnabled = WebInspector.settings.showCpuOnTimelineRuler.get();
+        if (this._mainThreadMonitoringEnabled !== mainThreadMonitoringEnabled) {
+            this._mainThreadMonitoringEnabled = mainThreadMonitoringEnabled;
+            this._refreshMainThreadBars();
+        }
+    },
+
+    /**
+     * @param {Event} event
+     * @return {boolean}
+     */
+    _startSplitterDragging: function(event)
+    {
+        this._dragOffset = this._timelineMemorySplitter.offsetTop + 2 - event.pageY;
+        return true;
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _splitterDragging: function(event)
+    {
+        var top = event.pageY + this._dragOffset
+        this._setSplitterPosition(top);
+        event.preventDefault();
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _endSplitterDragging: function(event)
+    {
+        delete this._dragOffset;
+        this._memoryStatistics.show();
+        WebInspector.settings.memoryCounterGraphsHeight.set(this.splitView.element.offsetHeight);
+    },
+
+    _setSplitterPosition: function(top)
+    {
+        const overviewHeight = 90;
+        const sectionMinHeight = 100;
+        top = Number.constrain(top, overviewHeight + sectionMinHeight, this.element.offsetHeight - sectionMinHeight);
+
+        this.splitView.element.style.height = (top - overviewHeight) + "px";
+        this._timelineMemorySplitter.style.top = (top - 2) + "px";
+        this._memoryStatistics.setTopPosition(top);
+        this._containerElementHeight = this._containerElement.clientHeight;
+        this.onResize();
+    },
+
+    get calculator()
+    {
+        return this._calculator;
+    },
+
+    get statusBarItems()
+    {
+        return this._statusBarItems.select("element").concat([
+            this._miscStatusBarItems
+        ]);
+    },
+
+    defaultFocusedElement: function()
+    {
+        return this.element;
+    },
+
+    _createStatusBarItems: function()
+    {
+        this._statusBarItems = /** @type {!Array.<!WebInspector.StatusBarItem>} */ ([]);
+
+        this.toggleTimelineButton = new WebInspector.StatusBarButton(WebInspector.UIString("Record"), "record-profile-status-bar-item");
+        this.toggleTimelineButton.addEventListener("click", this._toggleTimelineButtonClicked, this);
+        this._statusBarItems.push(this.toggleTimelineButton);
+
+        this.clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item");
+        this.clearButton.addEventListener("click", this._clearPanel, this);
+        this._statusBarItems.push(this.clearButton);
+
+        this.garbageCollectButton = new WebInspector.StatusBarButton(WebInspector.UIString("Collect Garbage"), "garbage-collect-status-bar-item");
+        this.garbageCollectButton.addEventListener("click", this._garbageCollectButtonClicked, this);
+        this._statusBarItems.push(this.garbageCollectButton);
+
+        this._glueParentButton = new WebInspector.StatusBarButton(WebInspector.UIString("Glue asynchronous events to causes"), "glue-async-status-bar-item");
+        this._glueParentButton.toggled = this._glueRecordsSetting.get();
+        this._presentationModel.setGlueRecords(this._glueParentButton.toggled);
+        this._glueParentButton.addEventListener("click", this._glueParentButtonClicked, this);
+        this._statusBarItems.push(this._glueParentButton);
+
+        this._durationFilterSelector = new WebInspector.StatusBarComboBox(this._durationFilterChanged.bind(this));
+        for (var presetIndex = 0; presetIndex < WebInspector.TimelinePanel.durationFilterPresetsMs.length; ++presetIndex) {
+            var durationMs = WebInspector.TimelinePanel.durationFilterPresetsMs[presetIndex];
+            var option = document.createElement("option");
+            if (!durationMs) {
+                option.text = WebInspector.UIString("All");
+                option.title = WebInspector.UIString("Show all records");
+            } else {
+                option.text = WebInspector.UIString("\u2265 %dms", durationMs);
+                option.title = WebInspector.UIString("Hide records shorter than %dms", durationMs);
+            }
+            option._durationMs = durationMs;
+            this._durationFilterSelector.addOption(option);
+            this._durationFilterSelector.element.title = this._durationFilterSelector.selectedOption().title;
+        }
+        this._statusBarItems.push(this._durationFilterSelector);
+
+        this._miscStatusBarItems = document.createElement("div");
+        this._miscStatusBarItems.className = "status-bar-items timeline-misc-status-bar-items";
+
+        this._statusBarFilters = this._miscStatusBarItems.createChild("div", "timeline-misc-status-bar-filters");
+        var categories = WebInspector.TimelinePresentationModel.categories();
+        for (var categoryName in categories) {
+            var category = categories[categoryName];
+            if (category.overviewStripGroupIndex < 0)
+                continue;
+            this._statusBarFilters.appendChild(this._createTimelineCategoryStatusBarCheckbox(category, this._onCategoryCheckboxClicked.bind(this, category)));
+        }
+
+        var statsContainer = this._statusBarFilters.createChild("div");
+        statsContainer.className = "timeline-records-stats-container";
+
+        this.recordsCounter = statsContainer.createChild("div");
+        this.recordsCounter.className = "timeline-records-stats";
+
+        this.frameStatistics = statsContainer.createChild("div");
+        this.frameStatistics.className = "timeline-records-stats hidden";
+
+        function getAnchor()
+        {
+            return this.frameStatistics;
+        }
+        this._frameStatisticsPopoverHelper = new WebInspector.PopoverHelper(this.frameStatistics, getAnchor.bind(this), this._showFrameStatistics.bind(this));
+    },
+
+    _createTimelineCategoryStatusBarCheckbox: function(category, onCheckboxClicked)
+    {
+        var labelContainer = document.createElement("div");
+        labelContainer.addStyleClass("timeline-category-statusbar-item");
+        labelContainer.addStyleClass("timeline-category-" + category.name);
+        labelContainer.addStyleClass("status-bar-item");
+
+        var label = document.createElement("label");
+        var checkBorder = label.createChild("div", "timeline-category-checkbox");
+        var checkElement = checkBorder.createChild("div", "timeline-category-checkbox-check timeline-category-checkbox-checked");
+        checkElement.type = "checkbox";
+        checkElement.checked = true;
+        checkElement.addEventListener("click", listener, false);
+
+        function listener(event)
+        {
+            checkElement.checked = !checkElement.checked;
+            checkElement.enableStyleClass("timeline-category-checkbox-checked", checkElement.checked);
+            onCheckboxClicked(event);
+        }
+
+        var typeElement = document.createElement("span");
+        typeElement.className = "type";
+        typeElement.textContent = category.title;
+        label.appendChild(typeElement);
+
+        labelContainer.appendChild(label);
+        return labelContainer;
+    },
+
+    _onCategoryCheckboxClicked: function(category, event)
+    {
+        category.hidden = !event.target.checked;
+        this._invalidateAndScheduleRefresh(true, true);
+    },
+
+    /**
+     * @param {?WebInspector.ProgressIndicator} indicator
+     */
+    _setOperationInProgress: function(indicator)
+    {
+        this._operationInProgress = !!indicator;
+        for (var i = 0; i < this._statusBarItems.length; ++i)
+            this._statusBarItems[i].setEnabled(!this._operationInProgress);
+        this._glueParentButton.setEnabled(!this._operationInProgress && !this._frameController);
+        this._miscStatusBarItems.removeChildren();
+        this._miscStatusBarItems.appendChild(indicator ? indicator.element : this._statusBarFilters);
+    },
+
+    _registerShortcuts: function()
+    {
+        this.registerShortcuts(WebInspector.TimelinePanelDescriptor.ShortcutKeys.StartStopRecording, this._toggleTimelineButtonClicked.bind(this));
+        this.registerShortcuts(WebInspector.TimelinePanelDescriptor.ShortcutKeys.SaveToFile, this._saveToFile.bind(this));
+        this.registerShortcuts(WebInspector.TimelinePanelDescriptor.ShortcutKeys.LoadFromFile, this._selectFileToLoad.bind(this));
+    },
+
+    _createFileSelector: function()
+    {
+        if (this._fileSelectorElement)
+            this.element.removeChild(this._fileSelectorElement);
+
+        this._fileSelectorElement = WebInspector.createFileSelectorElement(this._loadFromFile.bind(this));
+        this.element.appendChild(this._fileSelectorElement);
+    },
+
+    _contextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Save Timeline data\u2026" : "Save Timeline Data\u2026"), this._saveToFile.bind(this), this._operationInProgress);
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Load Timeline data\u2026" : "Load Timeline Data\u2026"), this._selectFileToLoad.bind(this), this._operationInProgress);
+        contextMenu.show();
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _saveToFile: function(event)
+    {
+        if (this._operationInProgress)
+            return true;
+        this._model.saveToFile();
+        return true;
+    },
+
+    /**
+     * @param {Event=} event
+     * @return {boolean}
+     */
+    _selectFileToLoad: function(event) {
+        this._fileSelectorElement.click();
+        return true;
+    },
+
+    /**
+     * @param {!File} file
+     */
+    _loadFromFile: function(file)
+    {
+        var progressIndicator = this._prepareToLoadTimeline();
+        if (!progressIndicator)
+            return;
+        this._model.loadFromFile(file, progressIndicator);
+        this._createFileSelector();
+    },
+
+    /**
+     * @param {string} url
+     */
+    loadFromURL: function(url)
+    {
+        var progressIndicator = this._prepareToLoadTimeline();
+        if (!progressIndicator)
+            return;
+        this._model.loadFromURL(url, progressIndicator);
+    },
+
+    /**
+     * @return {?WebInspector.ProgressIndicator}
+     */
+    _prepareToLoadTimeline: function()
+    {
+        if (this._operationInProgress)
+            return null;
+        if (this.toggleTimelineButton.toggled) {
+            this.toggleTimelineButton.toggled = false;
+            this._model.stopRecord();
+        }
+        var progressIndicator = new WebInspector.ProgressIndicator();
+        progressIndicator.addEventListener(WebInspector.ProgressIndicator.Events.Done, this._setOperationInProgress.bind(this, null));
+        this._setOperationInProgress(progressIndicator);
+        return progressIndicator;
+    },
+
+    _rootRecord: function()
+    {
+        return this._presentationModel.rootRecord();
+    },
+
+    _updateRecordsCounter: function(recordsInWindowCount)
+    {
+        this.recordsCounter.textContent = WebInspector.UIString("%d of %d records shown", recordsInWindowCount, this._allRecordsCount);
+    },
+
+    _updateFrameStatistics: function(frames)
+    {
+        if (frames.length) {
+            this._lastFrameStatistics = new WebInspector.FrameStatistics(frames);
+            var details = WebInspector.UIString("avg: %s, \u03c3: %s",
+                Number.secondsToString(this._lastFrameStatistics.average, true), Number.secondsToString(this._lastFrameStatistics.stddev, true));
+        } else
+            this._lastFrameStatistics = null;
+        this.frameStatistics.textContent = WebInspector.UIString("%d of %d frames shown", frames.length, this._presentationModel.frames().length);
+        if (details) {
+            this.frameStatistics.appendChild(document.createTextNode(" ("));
+            this.frameStatistics.createChild("span", "timeline-frames-stats").textContent = details;
+            this.frameStatistics.appendChild(document.createTextNode(")"));
+        }
+    },
+
+    /**
+     * @param {Element} anchor
+     * @param {WebInspector.Popover} popover
+     */
+    _showFrameStatistics: function(anchor, popover)
+    {
+        popover.show(WebInspector.TimelinePresentationModel.generatePopupContentForFrameStatistics(this._lastFrameStatistics), anchor);
+    },
+
+    _updateEventDividers: function()
+    {
+        this._timelineGrid.removeEventDividers();
+        var clientWidth = this._graphRowsElementWidth;
+        var dividers = [];
+        var eventDividerRecords = this._presentationModel.eventDividerRecords();
+
+        for (var i = 0; i < eventDividerRecords.length; ++i) {
+            var record = eventDividerRecords[i];
+            var positions = this._calculator.computeBarGraphWindowPosition(record);
+            var dividerPosition = Math.round(positions.left);
+            if (dividerPosition < 0 || dividerPosition >= clientWidth || dividers[dividerPosition])
+                continue;
+            var divider = WebInspector.TimelinePresentationModel.createEventDivider(record.type, record.title);
+            divider.style.left = dividerPosition + "px";
+            dividers[dividerPosition] = divider;
+        }
+        this._timelineGrid.addEventDividers(dividers);
+    },
+
+    _updateFrameBars: function(frames)
+    {
+        var clientWidth = this._graphRowsElementWidth;
+        if (this._frameContainer)
+            this._frameContainer.removeChildren();
+        else {
+            const frameContainerBorderWidth = 1;
+            this._frameContainer = document.createElement("div");
+            this._frameContainer.addStyleClass("fill");
+            this._frameContainer.addStyleClass("timeline-frame-container");
+            this._frameContainer.style.height = this._headerLineCount * WebInspector.TimelinePanel.rowHeight + frameContainerBorderWidth + "px";
+            this._frameContainer.addEventListener("dblclick", this._onFrameDoubleClicked.bind(this), false);
+        }
+
+        var dividers = [ this._frameContainer ];
+
+        for (var i = 0; i < frames.length; ++i) {
+            var frame = frames[i];
+            var frameStart = this._calculator.computePosition(frame.startTime);
+            var frameEnd = this._calculator.computePosition(frame.endTime);
+
+            var frameStrip = document.createElement("div");
+            frameStrip.className = "timeline-frame-strip";
+            var actualStart = Math.max(frameStart, 0);
+            var width = frameEnd - actualStart;
+            frameStrip.style.left = actualStart + "px";
+            frameStrip.style.width = width + "px";
+            frameStrip._frame = frame;
+
+            const minWidthForFrameInfo = 60;
+            if (width > minWidthForFrameInfo)
+                frameStrip.textContent = Number.secondsToString(frame.endTime - frame.startTime, true);
+
+            this._frameContainer.appendChild(frameStrip);
+
+            if (actualStart > 0) {
+                var frameMarker = WebInspector.TimelinePresentationModel.createEventDivider(WebInspector.TimelineModel.RecordType.BeginFrame);
+                frameMarker.style.left = frameStart + "px";
+                dividers.push(frameMarker);
+            }
+        }
+        this._timelineGrid.addEventDividers(dividers);
+    },
+
+    _onFrameDoubleClicked: function(event)
+    {
+        var frameBar = event.target.enclosingNodeOrSelfWithClass("timeline-frame-strip");
+        if (!frameBar)
+            return;
+        this._overviewPane.zoomToFrame(frameBar._frame);
+    },
+
+    _overviewModeChanged: function(event)
+    {
+        var mode = event.data;
+        var shouldShowMemory = mode === WebInspector.TimelineOverviewPane.Mode.Memory;
+        var frameMode = mode === WebInspector.TimelineOverviewPane.Mode.Frames;
+        this._overviewModeSetting.set(mode);
+        if (frameMode !== this._frameMode) {
+            this._frameMode = frameMode;
+            this._glueParentButton.setEnabled(!frameMode);
+            this._presentationModel.setGlueRecords(this._glueParentButton.toggled && !frameMode);
+            this._repopulateRecords();
+
+            if (frameMode) {
+                this.element.addStyleClass("timeline-frame-overview");
+                this.recordsCounter.addStyleClass("hidden");
+                this.frameStatistics.removeStyleClass("hidden");
+                this._frameController = new WebInspector.TimelineFrameController(this._model, this._overviewPane, this._presentationModel);
+            } else {
+                this._frameController.dispose();
+                this._frameController = null;
+                this.element.removeStyleClass("timeline-frame-overview");
+                this.recordsCounter.removeStyleClass("hidden");
+                this.frameStatistics.addStyleClass("hidden");
+            }
+        }
+        if (shouldShowMemory === this._memoryStatistics.visible())
+            return;
+        if (!shouldShowMemory) {
+            this._timelineMemorySplitter.addStyleClass("hidden");
+            this._memoryStatistics.hide();
+            this.splitView.element.style.height = "auto";
+            this.splitView.element.style.bottom = "0";
+            this.onResize();
+        } else {
+            this._timelineMemorySplitter.removeStyleClass("hidden");
+            this._memoryStatistics.show();
+            this.splitView.element.style.bottom = "auto";
+            this._setSplitterPosition(WebInspector.settings.memoryCounterGraphsHeight.get());
+        }
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _toggleTimelineButtonClicked: function()
+    {
+        if (this._operationInProgress)
+            return true;
+        if (this.toggleTimelineButton.toggled) {
+            this._model.stopRecord();
+            this.toggleTimelineButton.title = WebInspector.UIString("Record");
+        } else {
+            this._model.startRecord(this._includeDomCounters, this._includeNativeMemoryStatistics);
+            this.toggleTimelineButton.title = WebInspector.UIString("Stop");
+            WebInspector.userMetrics.TimelineStarted.record();
+        }
+        this.toggleTimelineButton.toggled = !this.toggleTimelineButton.toggled;
+        return true;
+    },
+
+    _durationFilterChanged: function()
+    {
+        var option = this._durationFilterSelector.selectedOption();
+        var minimumRecordDuration = +option._durationMs / 1000.0;
+        this._durationFilter.setMinimumRecordDuration(minimumRecordDuration);
+        this._durationFilterSelector.element.title = option.title;
+        this._invalidateAndScheduleRefresh(true, true);
+    },
+
+    _garbageCollectButtonClicked: function()
+    {
+        HeapProfilerAgent.collectGarbage();
+    },
+
+    _glueParentButtonClicked: function()
+    {
+        var newValue = !this._glueParentButton.toggled;
+        this._glueParentButton.toggled = newValue;
+        this._presentationModel.setGlueRecords(newValue);
+        this._glueRecordsSetting.set(newValue);
+        this._repopulateRecords();
+    },
+
+    _repopulateRecords: function()
+    {
+        this._resetPanel();
+        this._automaticallySizeWindow = false;
+        var records = this._model.records;
+        for (var i = 0; i < records.length; ++i)
+            this._innerAddRecordToTimeline(records[i]);
+        this._invalidateAndScheduleRefresh(false, true);
+    },
+
+    _onTimelineEventRecorded: function(event)
+    {
+        if (this._innerAddRecordToTimeline(event.data))
+            this._invalidateAndScheduleRefresh(false, false);
+    },
+
+    _innerAddRecordToTimeline: function(record)
+    {
+        if (record.type === WebInspector.TimelineModel.RecordType.Program) {
+            this._mainThreadTasks.push({
+                startTime: WebInspector.TimelineModel.startTimeInSeconds(record),
+                endTime: WebInspector.TimelineModel.endTimeInSeconds(record)
+            });
+        }
+
+        var records = this._presentationModel.addRecord(record);
+        this._allRecordsCount += records.length;
+        var hasVisibleRecords = false;
+        var presentationModel = this._presentationModel;
+        function checkVisible(record)
+        {
+            hasVisibleRecords |= presentationModel.isVisible(record);
+        }
+        WebInspector.TimelinePresentationModel.forAllRecords(records, checkVisible);
+
+        function isAdoptedRecord(record)
+        {
+            return record.parent !== presentationModel.rootRecord;
+        }
+        // Tell caller update is necessary either if we added a visible record or if we re-parented a record.
+        return hasVisibleRecords || records.some(isAdoptedRecord);
+    },
+
+    sidebarResized: function(event)
+    {
+        var width = event.data;
+        this._resize(width);
+        this._sidebarBackgroundElement.style.width = width + "px";
+        this._overviewPane.sidebarResized(width);
+        this._memoryStatistics.setSidebarWidth(width);
+        this._timelineGrid.gridHeaderElement.style.left = width + "px";
+    },
+
+    onResize: function()
+    {
+        this._resize(this.splitView.sidebarWidth());
+    },
+
+    /**
+     * @param {number} sidebarWidth
+     */
+    _resize: function(sidebarWidth)
+    {
+        this._closeRecordDetails();
+        this._scheduleRefresh(false, true);
+        this._graphRowsElementWidth = this._graphRowsElement.offsetWidth;
+        this._containerElementHeight = this._containerElement.clientHeight;
+        var lastItemElement = this._statusBarItems[this._statusBarItems.length - 1].element;
+        var minFloatingStatusBarItemsOffset = lastItemElement.totalOffsetLeft() + lastItemElement.offsetWidth;
+        this._timelineGrid.gridHeaderElement.style.width = this._itemsGraphsElement.offsetWidth + "px";
+        this._miscStatusBarItems.style.left = Math.max(minFloatingStatusBarItemsOffset, sidebarWidth) + "px";
+    },
+
+    _clearPanel: function()
+    {
+        this._model.reset();
+    },
+
+    _onRecordsCleared: function()
+    {
+        this._resetPanel();
+        this._invalidateAndScheduleRefresh(true, true);
+    },
+
+    _resetPanel: function()
+    {
+        this._presentationModel.reset();
+        this._boundariesAreValid = false;
+        this._adjustScrollPosition(0);
+        this._closeRecordDetails();
+        this._allRecordsCount = 0;
+        this._automaticallySizeWindow = true;
+        this._mainThreadTasks = [];
+    },
+
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return [this._containerElement];
+    },
+
+    wasShown: function()
+    {
+        WebInspector.Panel.prototype.wasShown.call(this);
+        if (!WebInspector.TimelinePanel._categoryStylesInitialized) {
+            WebInspector.TimelinePanel._categoryStylesInitialized = true;
+            this._injectCategoryStyles();
+        }
+        this._overviewPane.setMode(this._overviewModeSetting.get());
+        this._refresh();
+    },
+
+    willHide: function()
+    {
+        this._closeRecordDetails();
+        WebInspector.Panel.prototype.willHide.call(this);
+    },
+
+    _onScroll: function(event)
+    {
+        this._closeRecordDetails();
+        this._scrollTop = this._containerElement.scrollTop;
+        var dividersTop = Math.max(0, this._scrollTop);
+        this._timelineGrid.setScrollAndDividerTop(this._scrollTop, dividersTop);
+        this._scheduleRefresh(true, true);
+    },
+
+    /**
+     * @param {boolean} preserveBoundaries
+     * @param {boolean} userGesture
+     */
+    _invalidateAndScheduleRefresh: function(preserveBoundaries, userGesture)
+    {
+        this._presentationModel.invalidateFilteredRecords();
+        delete this._searchResults;
+        this._scheduleRefresh(preserveBoundaries, userGesture);
+    },
+
+    /**
+     * @param {boolean} preserveBoundaries
+     * @param {boolean} userGesture
+     */
+    _scheduleRefresh: function(preserveBoundaries, userGesture)
+    {
+        this._closeRecordDetails();
+        this._boundariesAreValid &= preserveBoundaries;
+
+        if (!this.isShowing())
+            return;
+
+        if (preserveBoundaries || userGesture)
+            this._refresh();
+        else {
+            if (!this._refreshTimeout)
+                this._refreshTimeout = setTimeout(this._refresh.bind(this), 300);
+        }
+    },
+
+    _refresh: function()
+    {
+        if (this._refreshTimeout) {
+            clearTimeout(this._refreshTimeout);
+            delete this._refreshTimeout;
+        }
+
+        this._timelinePaddingLeft = this._expandOffset;
+        this._calculator.setWindow(this._overviewPane.windowStartTime(), this._overviewPane.windowEndTime());
+        this._calculator.setDisplayWindow(this._timelinePaddingLeft, this._graphRowsElementWidth);
+
+        var recordsInWindowCount = this._refreshRecords();
+        this._updateRecordsCounter(recordsInWindowCount);
+        if (!this._boundariesAreValid) {
+            this._updateEventDividers();
+            var frames = this._frameController && this._presentationModel.filteredFrames(this._overviewPane.windowStartTime(), this._overviewPane.windowEndTime());
+            if (frames) {
+                this._updateFrameStatistics(frames);
+                const maxFramesForFrameBars = 30;
+                if  (frames.length && frames.length < maxFramesForFrameBars) {
+                    this._timelineGrid.removeDividers();
+                    this._updateFrameBars(frames);
+                } else
+                    this._timelineGrid.updateDividers(this._calculator);
+            } else
+                this._timelineGrid.updateDividers(this._calculator);
+            if (this._mainThreadMonitoringEnabled)
+                this._refreshMainThreadBars();
+        }
+        if (this._memoryStatistics.visible())
+            this._memoryStatistics.refresh();
+        this._boundariesAreValid = true;
+    },
+
+    revealRecordAt: function(time)
+    {
+        var recordToReveal;
+        function findRecordToReveal(record)
+        {
+            if (record.containsTime(time)) {
+                recordToReveal = record;
+                return true;
+            }
+            // If there is no record containing the time than use the latest one before that time.
+            if (!recordToReveal || record.endTime < time && recordToReveal.endTime < record.endTime)
+                recordToReveal = record;
+            return false;
+        }
+        WebInspector.TimelinePresentationModel.forAllRecords(this._presentationModel.rootRecord().children, null, findRecordToReveal);
+
+        // The record ends before the window left bound so scroll to the top.
+        if (!recordToReveal) {
+            this._containerElement.scrollTop = 0;
+            return;
+        }
+
+        this._revealRecord(recordToReveal);
+    },
+
+    _revealRecord: function(recordToReveal)
+    {
+        // Expand all ancestors.
+        for (var parent = recordToReveal.parent; parent !== this._rootRecord(); parent = parent.parent) {
+            if (!parent.collapsed)
+                continue;
+            this._presentationModel.invalidateFilteredRecords();
+            parent.collapsed = false;
+        }
+        var recordsInWindow = this._presentationModel.filteredRecords();
+        var index = recordsInWindow.indexOf(recordToReveal);
+        this._recordToHighlight = recordToReveal;
+        var oldScrollTop = this._containerElement.scrollTop;
+        this._containerElement.scrollTop = index * WebInspector.TimelinePanel.rowHeight;
+        // The record to highlight will be only kept for one refresh cycle, so make sure
+        // refresh is only called once, either via scroll event handler or directly, if
+        // we're not about to scroll.
+        if (this._containerElement.scrollTop === oldScrollTop)
+            this._refresh();
+    },
+
+    _refreshRecords: function()
+    {
+        var recordsInWindow = this._presentationModel.filteredRecords();
+
+        // Calculate the visible area.
+        var visibleTop = this._scrollTop;
+        var visibleBottom = visibleTop + this._containerElementHeight;
+
+        const rowHeight = WebInspector.TimelinePanel.rowHeight;
+
+        // Convert visible area to visible indexes. Always include top-level record for a visible nested record.
+        var startIndex = Math.max(0, Math.min(Math.floor(visibleTop / rowHeight) - this._headerLineCount, recordsInWindow.length - 1));
+        var endIndex = Math.min(recordsInWindow.length, Math.ceil(visibleBottom / rowHeight));
+        var lastVisibleLine = Math.max(0, Math.floor(visibleBottom / rowHeight) - this._headerLineCount);
+        if (this._automaticallySizeWindow && recordsInWindow.length > lastVisibleLine) {
+            this._automaticallySizeWindow = false;
+            // If we're at the top, always use real timeline start as a left window bound so that expansion arrow padding logic works.
+            var windowStartTime = startIndex ? recordsInWindow[startIndex].startTime : this._model.minimumRecordTime();
+            this._overviewPane.setWindowTimes(windowStartTime, recordsInWindow[Math.max(0, lastVisibleLine - 1)].endTime);
+            recordsInWindow = this._presentationModel.filteredRecords();
+            endIndex = Math.min(recordsInWindow.length, lastVisibleLine);
+        }
+
+        // Resize gaps first.
+        this._topGapElement.style.height = (startIndex * rowHeight) + "px";
+        this.sidebarTreeElement.style.height = ((startIndex + this._headerLineCount) * rowHeight) + "px";
+        this._bottomGapElement.style.height = (recordsInWindow.length - endIndex) * rowHeight + "px";
+
+        // Update visible rows.
+        var listRowElement = this._sidebarListElement.firstChild;
+        var width = this._graphRowsElementWidth;
+        this._itemsGraphsElement.removeChild(this._graphRowsElement);
+        var graphRowElement = this._graphRowsElement.firstChild;
+        var scheduleRefreshCallback = this._invalidateAndScheduleRefresh.bind(this, true, true);
+        this._itemsGraphsElement.removeChild(this._expandElements);
+        this._expandElements.removeChildren();
+
+        var highlightedRecord = this._recordToHighlight;
+        delete this._recordToHighlight;
+        var highlightedListRowElement;
+        var highlightedGraphRowElement;
+
+        for (var i = 0; i < endIndex; ++i) {
+            var record = recordsInWindow[i];
+            var isEven = !(i % 2);
+
+            if (i < startIndex) {
+                var lastChildIndex = i + record.visibleChildrenCount;
+                if (lastChildIndex >= startIndex && lastChildIndex < endIndex) {
+                    var expandElement = new WebInspector.TimelineExpandableElement(this._expandElements);
+                    var positions = this._calculator.computeBarGraphWindowPosition(record);
+                    expandElement._update(record, i, positions.left - this._expandOffset, positions.width);
+                }
+            } else {
+                if (!listRowElement) {
+                    listRowElement = new WebInspector.TimelineRecordListRow().element;
+                    this._sidebarListElement.appendChild(listRowElement);
+                }
+                if (!graphRowElement) {
+                    graphRowElement = new WebInspector.TimelineRecordGraphRow(this._itemsGraphsElement, scheduleRefreshCallback).element;
+                    this._graphRowsElement.appendChild(graphRowElement);
+                }
+
+                if (highlightedRecord === record) {
+                    highlightedListRowElement = listRowElement;
+                    highlightedGraphRowElement = graphRowElement;
+                }
+
+                listRowElement.row.update(record, isEven, visibleTop);
+                graphRowElement.row.update(record, isEven, this._calculator, this._expandOffset, i);
+
+                listRowElement = listRowElement.nextSibling;
+                graphRowElement = graphRowElement.nextSibling;
+            }
+        }
+
+        // Remove extra rows.
+        while (listRowElement) {
+            var nextElement = listRowElement.nextSibling;
+            listRowElement.row.dispose();
+            listRowElement = nextElement;
+        }
+        while (graphRowElement) {
+            var nextElement = graphRowElement.nextSibling;
+            graphRowElement.row.dispose();
+            graphRowElement = nextElement;
+        }
+
+        this._itemsGraphsElement.insertBefore(this._graphRowsElement, this._bottomGapElement);
+        this._itemsGraphsElement.appendChild(this._expandElements);
+        this._adjustScrollPosition((recordsInWindow.length + this._headerLineCount) * rowHeight);
+        this._updateSearchHighlight(false);
+
+        if (highlightedListRowElement) {
+            highlightedListRowElement.addStyleClass("highlighted-timeline-record");
+            highlightedGraphRowElement.addStyleClass("highlighted-timeline-record");
+        }
+
+        return recordsInWindow.length;
+    },
+
+    _refreshMainThreadBars: function()
+    {
+        const barOffset = 3;
+        const minGap = 3;
+
+        var minWidth = WebInspector.TimelineCalculator._minWidth;
+        var widthAdjustment = minWidth / 2;
+
+        var width = this._graphRowsElementWidth;
+        var boundarySpan = this._overviewPane.windowEndTime() - this._overviewPane.windowStartTime();
+        var scale = boundarySpan / (width - minWidth - this._timelinePaddingLeft);
+        var startTime = this._overviewPane.windowStartTime() - this._timelinePaddingLeft * scale;
+        var endTime = startTime + width * scale;
+
+        var tasks = this._mainThreadMonitoringEnabled ? this._mainThreadTasks : [];
+
+        function compareEndTime(value, task)
+        {
+            return value < task.endTime ? -1 : 1;
+        }
+
+        var taskIndex = insertionIndexForObjectInListSortedByFunction(startTime, tasks, compareEndTime);
+
+        var container = this._cpuBarsElement;
+        var element = container.firstChild;
+        var lastElement;
+        var lastLeft;
+        var lastRight;
+
+        for (; taskIndex < tasks.length; ++taskIndex) {
+            var task = tasks[taskIndex];
+            if (task.startTime > endTime)
+                break;
+
+            var left = Math.max(0, this._calculator.computePosition(task.startTime) + barOffset - widthAdjustment);
+            var right = Math.min(width, this._calculator.computePosition(task.endTime) + barOffset + widthAdjustment);
+
+            if (lastElement) {
+                var gap = Math.floor(left) - Math.ceil(lastRight);
+                if (gap < minGap) {
+                    lastRight = right;
+                    lastElement._tasksInfo.lastTaskIndex = taskIndex;
+                    continue;
+                }
+                lastElement.style.width = (lastRight - lastLeft) + "px";
+            }
+
+            if (!element)
+                element = container.createChild("div", "timeline-graph-bar");
+
+            element.style.left = left + "px";
+            element._tasksInfo = {tasks: tasks, firstTaskIndex: taskIndex, lastTaskIndex: taskIndex};
+            lastLeft = left;
+            lastRight = right;
+
+            lastElement = element;
+            element = element.nextSibling;
+        }
+
+        if (lastElement)
+            lastElement.style.width = (lastRight - lastLeft) + "px";
+
+        while (element) {
+            var nextElement = element.nextSibling;
+            element._tasksInfo = null;
+            container.removeChild(element);
+            element = nextElement;
+        }
+    },
+
+    _adjustHeaderHeight: function()
+    {
+        const headerBorderWidth = 1;
+        const headerMargin = 2;
+
+        var headerHeight = this._headerLineCount * WebInspector.TimelinePanel.rowHeight;
+        this.sidebarElement.firstChild.style.height = headerHeight + "px";
+        this._timelineGrid.dividersLabelBarElement.style.height = headerHeight + headerMargin + "px";
+        this._itemsGraphsElement.style.top = headerHeight + headerBorderWidth + "px";
+    },
+
+    _adjustScrollPosition: function(totalHeight)
+    {
+        // Prevent the container from being scrolled off the end.
+        if ((this._scrollTop + this._containerElementHeight) > totalHeight + 1)
+            this._containerElement.scrollTop = (totalHeight - this._containerElement.offsetHeight);
+    },
+
+    _getPopoverAnchor: function(element)
+    {
+        return element.enclosingNodeOrSelfWithClass("timeline-graph-bar") ||
+            element.enclosingNodeOrSelfWithClass("timeline-tree-item") ||
+            element.enclosingNodeOrSelfWithClass("timeline-frame-strip");
+    },
+
+    _mouseOut: function(e)
+    {
+        this._hideQuadHighlight();
+    },
+
+    /**
+     * @param {Event} e
+     */
+    _mouseMove: function(e)
+    {
+        var anchor = this._getPopoverAnchor(e.target);
+
+        if (anchor && anchor.row && anchor.row._record.highlightQuad)
+            this._highlightQuad(anchor.row._record.highlightQuad);
+        else
+            this._hideQuadHighlight();
+
+        if (anchor && anchor._tasksInfo) {
+            var offset = anchor.offsetLeft;
+            this._timelineGrid.showCurtains(offset >= 0 ? offset : 0, anchor.offsetWidth);
+        } else
+            this._timelineGrid.hideCurtains();
+    },
+
+    /**
+     * @param {Array.<number>} quad
+     */
+    _highlightQuad: function(quad)
+    {
+        if (this._highlightedQuad === quad)
+            return;
+        this._highlightedQuad = quad;
+        DOMAgent.highlightQuad(quad, WebInspector.Color.PageHighlight.Content.toProtocolRGBA(), WebInspector.Color.PageHighlight.ContentOutline.toProtocolRGBA());
+    },
+
+    _hideQuadHighlight: function()
+    {
+        if (this._highlightedQuad) {
+            delete this._highlightedQuad;
+            DOMAgent.hideHighlight();
+        }
+    },
+
+    /**
+     * @param {Element} anchor
+     * @param {WebInspector.Popover} popover
+     */
+    _showPopover: function(anchor, popover)
+    {
+        if (anchor.hasStyleClass("timeline-frame-strip")) {
+            var frame = anchor._frame;
+            popover.show(WebInspector.TimelinePresentationModel.generatePopupContentForFrame(frame), anchor);
+        } else {
+            if (anchor.row && anchor.row._record)
+                anchor.row._record.generatePopupContent(showCallback);
+            else if (anchor._tasksInfo)
+                popover.show(this._presentationModel.generateMainThreadBarPopupContent(anchor._tasksInfo), anchor, null, null, WebInspector.Popover.Orientation.Bottom);
+        }
+
+        function showCallback(popupContent)
+        {
+            popover.show(popupContent, anchor);
+        }
+    },
+
+    _closeRecordDetails: function()
+    {
+        this._popoverHelper.hidePopover();
+    },
+
+    _injectCategoryStyles: function()
+    {
+        var style = document.createElement("style");
+        var categories = WebInspector.TimelinePresentationModel.categories();
+
+        style.textContent = Object.values(categories).map(WebInspector.TimelinePresentationModel.createStyleRuleForCategory).join("\n");
+        document.head.appendChild(style);
+    },
+
+    jumpToNextSearchResult: function()
+    {
+        this._jumpToAdjacentRecord(1);
+    },
+
+    jumpToPreviousSearchResult: function()
+    {
+        this._jumpToAdjacentRecord(-1);
+    },
+
+    _jumpToAdjacentRecord: function(offset)
+    {
+        if (!this._searchResults || !this._searchResults.length || !this._selectedSearchResult)
+            return;
+        var index = this._searchResults.indexOf(this._selectedSearchResult);
+        index = (index + offset + this._searchResults.length) % this._searchResults.length;
+        this._selectSearchResult(index);
+        this._highlightSelectedSearchResult(true);
+    },
+
+    _selectSearchResult: function(index)
+    {
+        this._selectedSearchResult = this._searchResults[index];
+        WebInspector.searchController.updateCurrentMatchIndex(index, this);
+    },
+
+    _highlightSelectedSearchResult: function(revealRecord)
+    {
+        this._clearHighlight();
+        if (this._searchFilter)
+            return;
+
+        var record = this._selectedSearchResult;
+        if (!record)
+            return;
+
+        for (var element = this._sidebarListElement.firstChild; element; element = element.nextSibling) {
+            if (element.row._record === record) {
+                element.row.highlight(this._searchRegExp, this._highlightDomChanges);
+                return;
+            }
+        }
+
+        if (revealRecord)
+            this._revealRecord(record);
+    },
+
+    _clearHighlight: function()
+    {
+        if (this._highlightDomChanges)
+            WebInspector.revertDomChanges(this._highlightDomChanges);
+        this._highlightDomChanges = [];
+    },
+
+    /**
+     * @param {boolean} revealRecord
+     */
+    _updateSearchHighlight: function(revealRecord)
+    {
+        if (this._searchFilter || !this._searchRegExp) {
+            this._clearHighlight();
+            return;
+        }
+
+        if (!this._searchResults)
+            this._updateSearchResults();
+
+        this._highlightSelectedSearchResult(revealRecord);
+    },
+
+    _updateSearchResults: function()
+    {
+        var searchRegExp = this._searchRegExp;
+        if (!searchRegExp)
+            return;
+
+        var matches = [];
+        var presentationModel = this._presentationModel;
+
+        function processRecord(record)
+        {
+            if (presentationModel.isVisible(record) && WebInspector.TimelineRecordListRow.testContentMatching(record, searchRegExp))
+                matches.push(record);
+            return false;
+        }
+        WebInspector.TimelinePresentationModel.forAllRecords(presentationModel.rootRecord().children, processRecord);
+
+        var matchesCount = matches.length;
+        if (matchesCount) {
+            this._searchResults = matches;
+            WebInspector.searchController.updateSearchMatchesCount(matchesCount, this);
+
+            var selectedIndex = matches.indexOf(this._selectedSearchResult);
+            if (selectedIndex === -1)
+                selectedIndex = 0;
+            this._selectSearchResult(selectedIndex);
+        } else {
+            WebInspector.searchController.updateSearchMatchesCount(0, this);
+            delete this._selectedSearchResult;
+        }
+    },
+
+    searchCanceled: function()
+    {
+        this._clearHighlight();
+        delete this._searchResults;
+        delete this._selectedSearchResult;
+        delete this._searchRegExp;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canFilter: function()
+    {
+        return true;
+    },
+
+    performFilter: function(searchQuery)
+    {
+        this._presentationModel.removeFilter(this._searchFilter);
+        delete this._searchFilter;
+        this.searchCanceled();
+        if (searchQuery) {
+            this._searchFilter = new WebInspector.TimelineSearchFilter(createPlainTextSearchRegex(searchQuery, "i"));
+            this._presentationModel.addFilter(this._searchFilter);
+        }
+        this._invalidateAndScheduleRefresh(true, true);
+    },
+
+    performSearch: function(searchQuery)
+    {
+        this._searchRegExp = createPlainTextSearchRegex(searchQuery, "i");
+        delete this._searchResults;
+        this._updateSearchHighlight(true);
+    },
+
+    __proto__: WebInspector.Panel.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TimelineModel} model
+ * @implements {WebInspector.TimelineGrid.Calculator}
+ */
+WebInspector.TimelineCalculator = function(model)
+{
+    this._model = model;
+}
+
+WebInspector.TimelineCalculator._minWidth = 5;
+
+WebInspector.TimelineCalculator.prototype = {
+    /**
+     * @param {number} time
+     */
+    computePosition: function(time)
+    {
+        return (time - this._minimumBoundary) / this.boundarySpan() * this._workingArea + this.paddingLeft;
+    },
+
+    computeBarGraphPercentages: function(record)
+    {
+        var start = (record.startTime - this._minimumBoundary) / this.boundarySpan() * 100;
+        var end = (record.startTime + record.selfTime - this._minimumBoundary) / this.boundarySpan() * 100;
+        var endWithChildren = (record.lastChildEndTime - this._minimumBoundary) / this.boundarySpan() * 100;
+        var cpuWidth = record.coalesced ? endWithChildren - start : record.cpuTime / this.boundarySpan() * 100;
+        return {start: start, end: end, endWithChildren: endWithChildren, cpuWidth: cpuWidth};
+    },
+
+    computeBarGraphWindowPosition: function(record)
+    {
+        var percentages = this.computeBarGraphPercentages(record);
+        var widthAdjustment = 0;
+
+        var left = this.computePosition(record.startTime);
+        var width = (percentages.end - percentages.start) / 100 * this._workingArea;
+        if (width < WebInspector.TimelineCalculator._minWidth) {
+            widthAdjustment = WebInspector.TimelineCalculator._minWidth - width;
+            left -= widthAdjustment / 2;
+            width += widthAdjustment;
+        }
+        var widthWithChildren = (percentages.endWithChildren - percentages.start) / 100 * this._workingArea + widthAdjustment;
+        var cpuWidth = percentages.cpuWidth / 100 * this._workingArea + widthAdjustment;
+        if (percentages.endWithChildren > percentages.end)
+            widthWithChildren += widthAdjustment;
+        return {left: left, width: width, widthWithChildren: widthWithChildren, cpuWidth: cpuWidth};
+    },
+
+    setWindow: function(minimumBoundary, maximumBoundary)
+    {
+        this._minimumBoundary = minimumBoundary;
+        this._maximumBoundary = maximumBoundary;
+    },
+
+    /**
+     * @param {number} paddingLeft
+     * @param {number} clientWidth
+     */
+    setDisplayWindow: function(paddingLeft, clientWidth)
+    {
+        this._workingArea = clientWidth - WebInspector.TimelineCalculator._minWidth - paddingLeft;
+        this.paddingLeft = paddingLeft;
+    },
+
+    formatTime: function(value)
+    {
+        return Number.secondsToString(value + this._minimumBoundary - this._model.minimumRecordTime());
+    },
+
+    maximumBoundary: function()
+    {
+        return this._maximumBoundary;
+    },
+
+    minimumBoundary: function()
+    {
+        return this._minimumBoundary;
+    },
+
+    zeroTime: function()
+    {
+        return this._model.minimumRecordTime();
+    },
+
+    boundarySpan: function()
+    {
+        return this._maximumBoundary - this._minimumBoundary;
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.TimelineRecordListRow = function()
+{
+    this.element = document.createElement("div");
+    this.element.row = this;
+    this.element.style.cursor = "pointer";
+    var iconElement = document.createElement("span");
+    iconElement.className = "timeline-tree-icon";
+    this.element.appendChild(iconElement);
+
+    this._typeElement = document.createElement("span");
+    this._typeElement.className = "type";
+    this.element.appendChild(this._typeElement);
+
+    var separatorElement = document.createElement("span");
+    separatorElement.className = "separator";
+    separatorElement.textContent = " ";
+
+    this._dataElement = document.createElement("span");
+    this._dataElement.className = "data dimmed";
+
+    this.element.appendChild(separatorElement);
+    this.element.appendChild(this._dataElement);
+}
+
+WebInspector.TimelineRecordListRow.prototype = {
+    update: function(record, isEven, offset)
+    {
+        this._record = record;
+        this._offset = offset;
+
+        this.element.className = "timeline-tree-item timeline-category-" + record.category.name;
+        if (isEven)
+            this.element.addStyleClass("even");
+        if (record.hasWarning)
+            this.element.addStyleClass("warning");
+        else if (record.childHasWarning)
+            this.element.addStyleClass("child-warning");
+        if (record.isBackground)
+            this.element.addStyleClass("background");
+
+        this._typeElement.textContent = record.title;
+
+        if (this._dataElement.firstChild)
+            this._dataElement.removeChildren();
+
+        if (record.detailsNode())
+            this._dataElement.appendChild(record.detailsNode());
+    },
+
+    highlight: function(regExp, domChanges)
+    {
+        var matchInfo = this.element.textContent.match(regExp);
+        if (matchInfo)
+            WebInspector.highlightSearchResult(this.element, matchInfo.index, matchInfo[0].length, domChanges);
+    },
+
+    dispose: function()
+    {
+        this.element.parentElement.removeChild(this.element);
+    }
+}
+
+/**
+ * @param {!WebInspector.TimelinePresentationModel.Record} record
+ * @param {!RegExp} regExp
+ */
+WebInspector.TimelineRecordListRow.testContentMatching = function(record, regExp)
+{
+    var toSearchText = record.title;
+    if (record.detailsNode())
+        toSearchText += " " + record.detailsNode().textContent;
+    return regExp.test(toSearchText);
+}
+
+/**
+ * @constructor
+ */
+WebInspector.TimelineRecordGraphRow = function(graphContainer, scheduleRefresh)
+{
+    this.element = document.createElement("div");
+    this.element.row = this;
+
+    this._barAreaElement = document.createElement("div");
+    this._barAreaElement.className = "timeline-graph-bar-area";
+    this.element.appendChild(this._barAreaElement);
+
+    this._barWithChildrenElement = document.createElement("div");
+    this._barWithChildrenElement.className = "timeline-graph-bar with-children";
+    this._barWithChildrenElement.row = this;
+    this._barAreaElement.appendChild(this._barWithChildrenElement);
+
+    this._barCpuElement = document.createElement("div");
+    this._barCpuElement.className = "timeline-graph-bar cpu"
+    this._barCpuElement.row = this;
+    this._barAreaElement.appendChild(this._barCpuElement);
+
+    this._barElement = document.createElement("div");
+    this._barElement.className = "timeline-graph-bar";
+    this._barElement.row = this;
+    this._barAreaElement.appendChild(this._barElement);
+
+    this._expandElement = new WebInspector.TimelineExpandableElement(graphContainer);
+    this._expandElement._element.addEventListener("click", this._onClick.bind(this));
+
+    this._scheduleRefresh = scheduleRefresh;
+}
+
+WebInspector.TimelineRecordGraphRow.prototype = {
+    update: function(record, isEven, calculator, expandOffset, index)
+    {
+        this._record = record;
+        this.element.className = "timeline-graph-side timeline-category-" + record.category.name;
+        if (isEven)
+            this.element.addStyleClass("even");
+        if (record.isBackground)
+            this.element.addStyleClass("background");
+
+        var barPosition = calculator.computeBarGraphWindowPosition(record);
+        this._barWithChildrenElement.style.left = barPosition.left + "px";
+        this._barWithChildrenElement.style.width = barPosition.widthWithChildren + "px";
+        this._barElement.style.left = barPosition.left + "px";
+        this._barElement.style.width = barPosition.width + "px";
+        this._barCpuElement.style.left = barPosition.left + "px";
+        this._barCpuElement.style.width = barPosition.cpuWidth + "px";
+        this._expandElement._update(record, index, barPosition.left - expandOffset, barPosition.width);
+    },
+
+    _onClick: function(event)
+    {
+        this._record.collapsed = !this._record.collapsed;
+        this._scheduleRefresh(false, true);
+    },
+
+    dispose: function()
+    {
+        this.element.parentElement.removeChild(this.element);
+        this._expandElement._dispose();
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.TimelineExpandableElement = function(container)
+{
+    this._element = container.createChild("div", "timeline-expandable");
+    this._element.createChild("div", "timeline-expandable-left");
+    this._element.createChild("div", "timeline-expandable-arrow");
+}
+
+WebInspector.TimelineExpandableElement.prototype = {
+    _update: function(record, index, left, width)
+    {
+        const rowHeight = WebInspector.TimelinePanel.rowHeight;
+        if (record.visibleChildrenCount || record.invisibleChildrenCount) {
+            this._element.style.top = index * rowHeight + "px";
+            this._element.style.left = left + "px";
+            this._element.style.width = Math.max(12, width + 25) + "px";
+            if (!record.collapsed) {
+                this._element.style.height = (record.visibleChildrenCount + 1) * rowHeight + "px";
+                this._element.addStyleClass("timeline-expandable-expanded");
+                this._element.removeStyleClass("timeline-expandable-collapsed");
+            } else {
+                this._element.style.height = rowHeight + "px";
+                this._element.addStyleClass("timeline-expandable-collapsed");
+                this._element.removeStyleClass("timeline-expandable-expanded");
+            }
+            this._element.removeStyleClass("hidden");
+        } else
+            this._element.addStyleClass("hidden");
+    },
+
+    _dispose: function()
+    {
+        this._element.parentElement.removeChild(this._element);
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelinePresentationModel.Filter}
+ */
+WebInspector.TimelineCategoryFilter = function()
+{
+}
+
+WebInspector.TimelineCategoryFilter.prototype = {
+    /**
+     * @param {!WebInspector.TimelinePresentationModel.Record} record
+     * @return {boolean}
+     */
+    accept: function(record)
+    {
+        return !record.category.hidden && record.type !== WebInspector.TimelineModel.RecordType.BeginFrame;
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelinePresentationModel.Filter}
+ */
+WebInspector.TimelineIsLongFilter = function()
+{
+    this._minimumRecordDuration = 0;
+}
+
+WebInspector.TimelineIsLongFilter.prototype = {
+    /**
+     * @param {number} value
+     */
+    setMinimumRecordDuration: function(value)
+    {
+        this._minimumRecordDuration = value;
+    },
+
+    /**
+     * @param {!WebInspector.TimelinePresentationModel.Record} record
+     * @return {boolean}
+     */
+    accept: function(record)
+    {
+        return this._minimumRecordDuration ? ((record.lastChildEndTime - record.startTime) >= this._minimumRecordDuration) : true;
+    }
+}
+
+/**
+ * @param {!RegExp} regExp
+ * @constructor
+ * @implements {WebInspector.TimelinePresentationModel.Filter}
+ */
+WebInspector.TimelineSearchFilter = function(regExp)
+{
+    this._regExp = regExp;
+}
+
+WebInspector.TimelineSearchFilter.prototype = {
+
+    /**
+     * @param {!WebInspector.TimelinePresentationModel.Record} record
+     * @return {boolean}
+     */
+    accept: function(record)
+    {
+        return WebInspector.TimelineRecordListRow.testContentMatching(record, this._regExp);
+    }
+}
diff --git a/Source/devtools/front_end/TimelinePanelDescriptor.js b/Source/devtools/front_end/TimelinePanelDescriptor.js
new file mode 100644
index 0000000..55af813
--- /dev/null
+++ b/Source/devtools/front_end/TimelinePanelDescriptor.js
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.PanelDescriptor}
+ */
+WebInspector.TimelinePanelDescriptor = function()
+{
+    WebInspector.PanelDescriptor.call(this, "timeline", WebInspector.UIString("Timeline"), "TimelinePanel", "TimelinePanel.js");
+}
+
+WebInspector.TimelinePanelDescriptor.prototype = {
+    registerShortcuts: function()
+    {
+        var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Timeline Panel"));
+        section.addAlternateKeys(WebInspector.TimelinePanelDescriptor.ShortcutKeys.StartStopRecording, WebInspector.UIString("Start/stop recording"));
+        section.addAlternateKeys(WebInspector.TimelinePanelDescriptor.ShortcutKeys.SaveToFile, WebInspector.UIString("Save timeline data"));
+        section.addAlternateKeys(WebInspector.TimelinePanelDescriptor.ShortcutKeys.LoadFromFile, WebInspector.UIString("Load timeline data"));
+    },
+
+    __proto__: WebInspector.PanelDescriptor.prototype
+}
+
+WebInspector.TimelinePanelDescriptor.ShortcutKeys = {
+    StartStopRecording: [
+        WebInspector.KeyboardShortcut.makeDescriptor("e", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ],
+
+    SaveToFile: [
+        WebInspector.KeyboardShortcut.makeDescriptor("s", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ],
+
+    LoadFromFile: [
+        WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)
+    ]
+}
diff --git a/Source/devtools/front_end/TimelinePresentationModel.js b/Source/devtools/front_end/TimelinePresentationModel.js
new file mode 100644
index 0000000..64bff61
--- /dev/null
+++ b/Source/devtools/front_end/TimelinePresentationModel.js
@@ -0,0 +1,1493 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2012 Intel 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.TimelinePresentationModel = function()
+{
+    this._linkifier = new WebInspector.Linkifier();
+    this._glueRecords = false;
+    this._filters = [];
+    this.reset();
+}
+
+WebInspector.TimelinePresentationModel.categories = function()
+{
+    if (WebInspector.TimelinePresentationModel._categories)
+        return WebInspector.TimelinePresentationModel._categories;
+    WebInspector.TimelinePresentationModel._categories = {
+        loading: new WebInspector.TimelineCategory("loading", WebInspector.UIString("Loading"), 0, "#5A8BCC", "#8EB6E9", "#70A2E3"),
+        scripting: new WebInspector.TimelineCategory("scripting", WebInspector.UIString("Scripting"), 1, "#D8AA34", "#F3D07A", "#F1C453"),
+        rendering: new WebInspector.TimelineCategory("rendering", WebInspector.UIString("Rendering"), 2, "#8266CC", "#AF9AEB", "#9A7EE6"),
+        painting: new WebInspector.TimelineCategory("painting", WebInspector.UIString("Painting"), 2, "#5FA050", "#8DC286", "#71B363"),
+        other: new WebInspector.TimelineCategory("other", WebInspector.UIString("Other"), -1, "#BBBBBB", "#DDDDDD", "#DDDDDD")
+    };
+    return WebInspector.TimelinePresentationModel._categories;
+};
+
+/**
+ * @return {!Object.<string, {title: string, category}>}
+ */
+WebInspector.TimelinePresentationModel._initRecordStyles = function()
+{
+    if (WebInspector.TimelinePresentationModel._recordStylesMap)
+        return WebInspector.TimelinePresentationModel._recordStylesMap;
+
+    var recordTypes = WebInspector.TimelineModel.RecordType;
+    var categories = WebInspector.TimelinePresentationModel.categories();
+
+    var recordStyles = {};
+    recordStyles[recordTypes.Root] = { title: "#root", category: categories["loading"] };
+    recordStyles[recordTypes.Program] = { title: WebInspector.UIString("Other"), category: categories["other"] };
+    recordStyles[recordTypes.EventDispatch] = { title: WebInspector.UIString("Event"), category: categories["scripting"] };
+    recordStyles[recordTypes.BeginFrame] = { title: WebInspector.UIString("Frame Start"), category: categories["rendering"] };
+    recordStyles[recordTypes.ScheduleStyleRecalculation] = { title: WebInspector.UIString("Schedule Style Recalculation"), category: categories["rendering"] };
+    recordStyles[recordTypes.RecalculateStyles] = { title: WebInspector.UIString("Recalculate Style"), category: categories["rendering"] };
+    recordStyles[recordTypes.InvalidateLayout] = { title: WebInspector.UIString("Invalidate Layout"), category: categories["rendering"] };
+    recordStyles[recordTypes.Layout] = { title: WebInspector.UIString("Layout"), category: categories["rendering"] };
+    recordStyles[recordTypes.Paint] = { title: WebInspector.UIString("Paint"), category: categories["painting"] };
+    recordStyles[recordTypes.Rasterize] = { title: WebInspector.UIString("Rasterize"), category: categories["painting"] };
+    recordStyles[recordTypes.ScrollLayer] = { title: WebInspector.UIString("Scroll"), category: categories["rendering"] };
+    recordStyles[recordTypes.DecodeImage] = { title: WebInspector.UIString("Image Decode"), category: categories["painting"] };
+    recordStyles[recordTypes.ResizeImage] = { title: WebInspector.UIString("Image Resize"), category: categories["painting"] };
+    recordStyles[recordTypes.CompositeLayers] = { title: WebInspector.UIString("Composite Layers"), category: categories["painting"] };
+    recordStyles[recordTypes.ParseHTML] = { title: WebInspector.UIString("Parse HTML"), category: categories["loading"] };
+    recordStyles[recordTypes.TimerInstall] = { title: WebInspector.UIString("Install Timer"), category: categories["scripting"] };
+    recordStyles[recordTypes.TimerRemove] = { title: WebInspector.UIString("Remove Timer"), category: categories["scripting"] };
+    recordStyles[recordTypes.TimerFire] = { title: WebInspector.UIString("Timer Fired"), category: categories["scripting"] };
+    recordStyles[recordTypes.XHRReadyStateChange] = { title: WebInspector.UIString("XHR Ready State Change"), category: categories["scripting"] };
+    recordStyles[recordTypes.XHRLoad] = { title: WebInspector.UIString("XHR Load"), category: categories["scripting"] };
+    recordStyles[recordTypes.EvaluateScript] = { title: WebInspector.UIString("Evaluate Script"), category: categories["scripting"] };
+    recordStyles[recordTypes.ResourceSendRequest] = { title: WebInspector.UIString("Send Request"), category: categories["loading"] };
+    recordStyles[recordTypes.ResourceReceiveResponse] = { title: WebInspector.UIString("Receive Response"), category: categories["loading"] };
+    recordStyles[recordTypes.ResourceFinish] = { title: WebInspector.UIString("Finish Loading"), category: categories["loading"] };
+    recordStyles[recordTypes.FunctionCall] = { title: WebInspector.UIString("Function Call"), category: categories["scripting"] };
+    recordStyles[recordTypes.ResourceReceivedData] = { title: WebInspector.UIString("Receive Data"), category: categories["loading"] };
+    recordStyles[recordTypes.GCEvent] = { title: WebInspector.UIString("GC Event"), category: categories["scripting"] };
+    recordStyles[recordTypes.MarkDOMContent] = { title: WebInspector.UIString("DOMContentLoaded event"), category: categories["scripting"] };
+    recordStyles[recordTypes.MarkLoad] = { title: WebInspector.UIString("Load event"), category: categories["scripting"] };
+    recordStyles[recordTypes.TimeStamp] = { title: WebInspector.UIString("Stamp"), category: categories["scripting"] };
+    recordStyles[recordTypes.Time] = { title: WebInspector.UIString("Time"), category: categories["scripting"] };
+    recordStyles[recordTypes.TimeEnd] = { title: WebInspector.UIString("Time End"), category: categories["scripting"] };
+    recordStyles[recordTypes.ScheduleResourceRequest] = { title: WebInspector.UIString("Schedule Request"), category: categories["loading"] };
+    recordStyles[recordTypes.RequestAnimationFrame] = { title: WebInspector.UIString("Request Animation Frame"), category: categories["scripting"] };
+    recordStyles[recordTypes.CancelAnimationFrame] = { title: WebInspector.UIString("Cancel Animation Frame"), category: categories["scripting"] };
+    recordStyles[recordTypes.FireAnimationFrame] = { title: WebInspector.UIString("Animation Frame Fired"), category: categories["scripting"] };
+    recordStyles[recordTypes.WebSocketCreate] = { title: WebInspector.UIString("Create WebSocket"), category: categories["scripting"] };
+    recordStyles[recordTypes.WebSocketSendHandshakeRequest] = { title: WebInspector.UIString("Send WebSocket Handshake"), category: categories["scripting"] };
+    recordStyles[recordTypes.WebSocketReceiveHandshakeResponse] = { title: WebInspector.UIString("Receive WebSocket Handshake"), category: categories["scripting"] };
+    recordStyles[recordTypes.WebSocketDestroy] = { title: WebInspector.UIString("Destroy WebSocket"), category: categories["scripting"] };
+
+    WebInspector.TimelinePresentationModel._recordStylesMap = recordStyles;
+    return recordStyles;
+}
+
+/**
+ * @param {Object} record
+ */
+WebInspector.TimelinePresentationModel.recordStyle = function(record)
+{
+    var recordStyles = WebInspector.TimelinePresentationModel._initRecordStyles();
+    var result = recordStyles[record.type];
+    if (!result) {
+        result = {
+            title: WebInspector.UIString("Unknown: %s", record.type),
+            category: WebInspector.TimelinePresentationModel.categories()["other"]
+        };
+        recordStyles[record.type] = result;
+    }
+    return result;
+}
+
+WebInspector.TimelinePresentationModel.categoryForRecord = function(record)
+{
+    return WebInspector.TimelinePresentationModel.recordStyle(record).category;
+}
+
+WebInspector.TimelinePresentationModel.isEventDivider = function(record)
+{
+    var recordTypes = WebInspector.TimelineModel.RecordType;
+    if (record.type === recordTypes.TimeStamp)
+        return true;
+    if (record.type === recordTypes.MarkDOMContent || record.type === recordTypes.MarkLoad) {
+        if (record.data && ((typeof record.data.isMainFrame) === "boolean"))
+            return record.data.isMainFrame;
+    }
+    return false;
+}
+
+/**
+ * @param {Array} recordsArray
+ * @param {?function(*)} preOrderCallback
+ * @param {function(*)=} postOrderCallback
+ */
+WebInspector.TimelinePresentationModel.forAllRecords = function(recordsArray, preOrderCallback, postOrderCallback)
+{
+    if (!recordsArray)
+        return;
+    var stack = [{array: recordsArray, index: 0}];
+    while (stack.length) {
+        var entry = stack[stack.length - 1];
+        var records = entry.array;
+        if (entry.index < records.length) {
+             var record = records[entry.index];
+             if (preOrderCallback && preOrderCallback(record))
+                 return;
+             if (record.children)
+                 stack.push({array: record.children, index: 0, record: record});
+             else if (postOrderCallback && postOrderCallback(record))
+                return;
+             ++entry.index;
+        } else {
+            if (entry.record && postOrderCallback && postOrderCallback(entry.record))
+                return;
+            stack.pop();
+        }
+    }
+}
+
+/**
+ * @param {string=} recordType
+ * @return {boolean}
+ */
+WebInspector.TimelinePresentationModel.needsPreviewElement = function(recordType)
+{
+    if (!recordType)
+        return false;
+    const recordTypes = WebInspector.TimelineModel.RecordType;
+    switch (recordType) {
+    case recordTypes.ScheduleResourceRequest:
+    case recordTypes.ResourceSendRequest:
+    case recordTypes.ResourceReceiveResponse:
+    case recordTypes.ResourceReceivedData:
+    case recordTypes.ResourceFinish:
+        return true;
+    default:
+        return false;
+    }
+}
+
+/**
+ * @param {string} recordType
+ * @param {string=} title
+ */
+WebInspector.TimelinePresentationModel.createEventDivider = function(recordType, title)
+{
+    var eventDivider = document.createElement("div");
+    eventDivider.className = "resources-event-divider";
+    var recordTypes = WebInspector.TimelineModel.RecordType;
+
+    if (recordType === recordTypes.MarkDOMContent)
+        eventDivider.className += " resources-blue-divider";
+    else if (recordType === recordTypes.MarkLoad)
+        eventDivider.className += " resources-red-divider";
+    else if (recordType === recordTypes.TimeStamp)
+        eventDivider.className += " resources-orange-divider";
+    else if (recordType === recordTypes.BeginFrame)
+        eventDivider.className += " timeline-frame-divider";
+
+    if (title)
+        eventDivider.title = title;
+
+    return eventDivider;
+}
+
+WebInspector.TimelinePresentationModel._hiddenRecords = { }
+WebInspector.TimelinePresentationModel._hiddenRecords[WebInspector.TimelineModel.RecordType.MarkDOMContent] = 1;
+WebInspector.TimelinePresentationModel._hiddenRecords[WebInspector.TimelineModel.RecordType.MarkLoad] = 1;
+WebInspector.TimelinePresentationModel._hiddenRecords[WebInspector.TimelineModel.RecordType.ScheduleStyleRecalculation] = 1;
+WebInspector.TimelinePresentationModel._hiddenRecords[WebInspector.TimelineModel.RecordType.InvalidateLayout] = 1;
+
+WebInspector.TimelinePresentationModel.prototype = {
+    /**
+     * @param {!WebInspector.TimelinePresentationModel.Filter} filter
+     */
+    addFilter: function(filter)
+    {
+        this._filters.push(filter);
+    },
+
+    /**
+     * @param {!WebInspector.TimelinePresentationModel.Filter} filter
+     */
+    removeFilter: function(filter)
+    {
+        var index = this._filters.indexOf(filter);
+        if (index !== -1)
+            this._filters.splice(index, 1);
+    },
+
+    rootRecord: function()
+    {
+        return this._rootRecord;
+    },
+
+    frames: function()
+    {
+        return this._frames;
+    },
+
+    reset: function()
+    {
+        this._linkifier.reset();
+        this._rootRecord = new WebInspector.TimelinePresentationModel.Record(this, { type: WebInspector.TimelineModel.RecordType.Root }, null, null, null, false);
+        this._sendRequestRecords = {};
+        this._scheduledResourceRequests = {};
+        this._timerRecords = {};
+        this._requestAnimationFrameRecords = {};
+        this._eventDividerRecords = [];
+        this._timeRecords = {};
+        this._timeRecordStack = [];
+        this._frames = [];
+        this._minimumRecordTime = -1;
+        this._layoutInvalidateStack = {};
+        this._lastScheduleStyleRecalculation = {};
+        this._webSocketCreateRecords = {};
+        this._coalescingBuckets = {};
+    },
+
+    addFrame: function(frame)
+    {
+        this._frames.push(frame);
+    },
+
+    addRecord: function(record)
+    {
+        if (this._minimumRecordTime === -1 || record.startTime < this._minimumRecordTime)
+            this._minimumRecordTime = WebInspector.TimelineModel.startTimeInSeconds(record);
+
+        var records;
+        if (record.type === WebInspector.TimelineModel.RecordType.Program)
+            records = record.children;
+        else
+            records = [record];
+
+        var formattedRecords = [];
+        var recordsCount = records.length;
+        for (var i = 0; i < recordsCount; ++i)
+            formattedRecords.push(this._innerAddRecord(records[i], this._rootRecord));
+        return formattedRecords;
+    },
+
+    _innerAddRecord: function(record, parentRecord)
+    {
+        const recordTypes = WebInspector.TimelineModel.RecordType;
+        var isHiddenRecord = record.type in WebInspector.TimelinePresentationModel._hiddenRecords;
+        var origin;
+        var coalescingBucket;
+
+        if (!isHiddenRecord) {
+            var newParentRecord = this._findParentRecord(record);
+            if (newParentRecord) {
+                origin = parentRecord;
+                parentRecord = newParentRecord;
+            }
+            // On main thread, only coalesce if the last event is of same type.
+            if (parentRecord === this._rootRecord)
+                coalescingBucket = record.thread ? record.type : "mainThread";
+            var coalescedRecord = this._findCoalescedParent(record, parentRecord, coalescingBucket);
+            if (coalescedRecord) {
+                if (!origin)
+                    origin = parentRecord;
+                parentRecord = coalescedRecord;
+            }
+        }
+
+        var children = record.children;
+        var scriptDetails;
+        if (record.data && record.data["scriptName"]) {
+            scriptDetails = {
+                scriptName: record.data["scriptName"],
+                scriptLine: record.data["scriptLine"]
+            }
+        };
+
+        if ((record.type === recordTypes.TimerFire || record.type === recordTypes.FireAnimationFrame) && children && children.length) {
+            var childRecord = children[0];
+            if (childRecord.type === recordTypes.FunctionCall) {
+                scriptDetails = {
+                    scriptName: childRecord.data["scriptName"],
+                    scriptLine: childRecord.data["scriptLine"]
+                };
+                children = childRecord.children.concat(children.slice(1));
+            }
+        }
+
+        var formattedRecord = new WebInspector.TimelinePresentationModel.Record(this, record, parentRecord, origin, scriptDetails, isHiddenRecord);
+
+        if (WebInspector.TimelinePresentationModel.isEventDivider(formattedRecord))
+            this._eventDividerRecords.push(formattedRecord);
+
+        if (isHiddenRecord)
+            return formattedRecord;
+
+        formattedRecord.collapsed = parentRecord === this._rootRecord;
+        if (coalescingBucket)
+            this._coalescingBuckets[coalescingBucket] = formattedRecord;
+
+        var childrenCount = children ? children.length : 0;
+        for (var i = 0; i < childrenCount; ++i)
+            this._innerAddRecord(children[i], formattedRecord);
+
+        formattedRecord.calculateAggregatedStats();
+
+        if (origin)
+            this._updateAncestorStats(formattedRecord);
+
+        if (parentRecord.coalesced && parentRecord.startTime > formattedRecord.startTime)
+            parentRecord._record.startTime = record.startTime;
+
+        origin = formattedRecord.origin();
+        if (!origin.isRoot() && !origin.coalesced)
+            origin.selfTime -= formattedRecord.endTime - formattedRecord.startTime;
+        return formattedRecord;
+    },
+
+    /**
+     * @param {WebInspector.TimelinePresentationModel.Record} record
+     */
+    _updateAncestorStats: function(record)
+    {
+        var lastChildEndTime = record.lastChildEndTime;
+        var aggregatedStats = record.aggregatedStats;
+        for (var currentRecord = record.parent; currentRecord && !currentRecord.isRoot(); currentRecord = currentRecord.parent) {
+            currentRecord._cpuTime += record._cpuTime;
+            if (currentRecord.lastChildEndTime < lastChildEndTime)
+                currentRecord.lastChildEndTime = lastChildEndTime;
+            for (var category in aggregatedStats)
+                currentRecord.aggregatedStats[category] += aggregatedStats[category];
+        }
+    },
+
+    /**
+     * @param {Object} record
+     * @param {Object} newParent
+     * @param {String} bucket
+     * @return {WebInspector.TimelinePresentationModel.Record?}
+     */
+    _findCoalescedParent: function(record, newParent, bucket)
+    {
+        const coalescingThresholdSeconds = 0.005;
+
+        var lastRecord = bucket ? this._coalescingBuckets[bucket] : newParent.children.peekLast();
+        if (lastRecord && lastRecord.coalesced)
+            lastRecord = lastRecord.children.peekLast();
+        var startTime = WebInspector.TimelineModel.startTimeInSeconds(record);
+        var endTime = WebInspector.TimelineModel.endTimeInSeconds(record);
+        if (!lastRecord || lastRecord.type !== record.type)
+            return null;
+        if (lastRecord.endTime + coalescingThresholdSeconds < startTime)
+            return null;
+        if (endTime + coalescingThresholdSeconds < lastRecord.startTime)
+            return null;
+        if (lastRecord.parent.coalesced)
+            return lastRecord.parent;
+        return this._replaceWithCoalescedRecord(lastRecord);
+    },
+
+    /**
+     * @param {WebInspector.TimelinePresentationModel.Record} record
+     * @return {WebInspector.TimelinePresentationModel.Record}
+     */
+    _replaceWithCoalescedRecord: function(record)
+    {
+        var rawRecord = {
+            type: record._record.type,
+            startTime: record._record.startTime,
+            endTime: record._record.endTime,
+            data: { }
+        };
+        if (record._record.thread)
+            rawRecord.thread = "aggregated";
+        var coalescedRecord = new WebInspector.TimelinePresentationModel.Record(this, rawRecord, null, null, null, false);
+        var parent = record.parent;
+
+        coalescedRecord.coalesced = true;
+        coalescedRecord.collapsed = true;
+        coalescedRecord._children.push(record);
+        record.parent = coalescedRecord;
+        coalescedRecord.calculateAggregatedStats();
+        if (record.hasWarning || record.childHasWarning)
+            coalescedRecord.childHasWarning = true;
+
+        coalescedRecord.parent = parent;
+        parent._children[parent._children.indexOf(record)] = coalescedRecord;
+        return coalescedRecord;
+    },
+
+    _findParentRecord: function(record)
+    {
+        if (!this._glueRecords)
+            return null;
+        var recordTypes = WebInspector.TimelineModel.RecordType;
+
+        switch (record.type) {
+        case recordTypes.ResourceReceiveResponse:
+        case recordTypes.ResourceFinish:
+        case recordTypes.ResourceReceivedData:
+            return this._sendRequestRecords[record.data["requestId"]];
+
+        case recordTypes.ResourceSendRequest:
+            return this._rootRecord;
+
+        case recordTypes.TimerFire:
+            return this._timerRecords[record.data["timerId"]];
+
+        case recordTypes.ResourceSendRequest:
+            return this._scheduledResourceRequests[record.data["url"]];
+
+        case recordTypes.FireAnimationFrame:
+            return this._requestAnimationFrameRecords[record.data["id"]];
+
+        case recordTypes.Time:
+            return this._rootRecord;
+
+        case recordTypes.TimeEnd:
+            return this._timeRecords[record.data["message"]];
+        }
+    },
+
+    setGlueRecords: function(glue)
+    {
+        this._glueRecords = glue;
+    },
+
+    invalidateFilteredRecords: function()
+    {
+        delete this._filteredRecords;
+    },
+
+    filteredRecords: function()
+    {
+        if (this._filteredRecords)
+            return this._filteredRecords;
+
+        var recordsInWindow = [];
+
+        var stack = [{children: this._rootRecord.children, index: 0, parentIsCollapsed: false}];
+        while (stack.length) {
+            var entry = stack[stack.length - 1];
+            var records = entry.children;
+            if (records && entry.index < records.length) {
+                 var record = records[entry.index];
+                 ++entry.index;
+
+                 if (this.isVisible(record)) {
+                     ++record.parent._invisibleChildrenCount;
+                     if (!entry.parentIsCollapsed)
+                         recordsInWindow.push(record);
+                 }
+
+                 record._invisibleChildrenCount = 0;
+
+                 stack.push({children: record.children,
+                             index: 0,
+                             parentIsCollapsed: (entry.parentIsCollapsed || record.collapsed),
+                             parentRecord: record,
+                             windowLengthBeforeChildrenTraversal: recordsInWindow.length});
+            } else {
+                stack.pop();
+                if (entry.parentRecord)
+                    entry.parentRecord._visibleChildrenCount = recordsInWindow.length - entry.windowLengthBeforeChildrenTraversal;
+            }
+        }
+
+        this._filteredRecords = recordsInWindow;
+        return recordsInWindow;
+    },
+
+    filteredFrames: function(startTime, endTime)
+    {
+        function compareStartTime(value, object)
+        {
+            return value - object.startTime;
+        }
+        function compareEndTime(value, object)
+        {
+            return value - object.endTime;
+        }
+        var firstFrame = insertionIndexForObjectInListSortedByFunction(startTime, this._frames, compareStartTime);
+        var lastFrame = insertionIndexForObjectInListSortedByFunction(endTime, this._frames, compareEndTime);
+        while (lastFrame < this._frames.length && this._frames[lastFrame].endTime <= endTime)
+            ++lastFrame;
+        return this._frames.slice(firstFrame, lastFrame);
+    },
+
+    eventDividerRecords: function()
+    {
+        return this._eventDividerRecords;
+    },
+
+    isVisible: function(record)
+    {
+        for (var i = 0; i < this._filters.length; ++i) {
+            if (!this._filters[i].accept(record))
+                return false;
+        }
+        return true;
+    },
+
+    /**
+     * @param {{tasks: !Array.<{startTime: number, endTime: number}>, firstTaskIndex: number, lastTaskIndex: number}} info
+     * @return {!Element}
+     */
+    generateMainThreadBarPopupContent: function(info)
+    {
+        var firstTaskIndex = info.firstTaskIndex;
+        var lastTaskIndex = info.lastTaskIndex;
+        var tasks = info.tasks;
+        var messageCount = lastTaskIndex - firstTaskIndex + 1;
+        var cpuTime = 0;
+
+        for (var i = firstTaskIndex; i <= lastTaskIndex; ++i) {
+            var task = tasks[i];
+            cpuTime += task.endTime - task.startTime;
+        }
+        var startTime = tasks[firstTaskIndex].startTime;
+        var endTime = tasks[lastTaskIndex].endTime;
+        var duration = endTime - startTime;
+        var offset = this._minimumRecordTime;
+
+        var contentHelper = new WebInspector.PopoverContentHelper(WebInspector.UIString("CPU"));
+        var durationText = WebInspector.UIString("%s (at %s)", Number.secondsToString(duration, true),
+            Number.secondsToString(startTime - offset, true));
+        contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
+        contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.secondsToString(cpuTime, true));
+        contentHelper.appendTextRow(WebInspector.UIString("Message Count"), messageCount);
+        return contentHelper.contentTable();
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.TimelinePresentationModel} presentationModel
+ * @param {Object} record
+ * @param {WebInspector.TimelinePresentationModel.Record} parentRecord
+ * @param {WebInspector.TimelinePresentationModel.Record} origin
+ * @param {Object|undefined} scriptDetails
+ * @param {boolean} hidden
+ */
+WebInspector.TimelinePresentationModel.Record = function(presentationModel, record, parentRecord, origin, scriptDetails, hidden)
+{
+    this._linkifier = presentationModel._linkifier;
+    this._aggregatedStats = {};
+    this._record = record;
+    this._children = [];
+    if (!hidden && parentRecord) {
+        this.parent = parentRecord;
+        if (this.isBackground)
+            WebInspector.TimelinePresentationModel.insertRetrospectiveRecord(parentRecord, this);
+        else
+            parentRecord.children.push(this);
+    }
+    if (origin)
+        this._origin = origin;
+
+    this._selfTime = this.endTime - this.startTime;
+    this._lastChildEndTime = this.endTime;
+    this._startTimeOffset = this.startTime - presentationModel._minimumRecordTime;
+
+    if (record.data && record.data["url"])
+        this.url = record.data["url"];
+    if (scriptDetails) {
+        this.scriptName = scriptDetails.scriptName;
+        this.scriptLine = scriptDetails.scriptLine;
+    }
+    if (parentRecord && parentRecord.callSiteStackTrace)
+        this.callSiteStackTrace = parentRecord.callSiteStackTrace;
+
+    var recordTypes = WebInspector.TimelineModel.RecordType;
+    switch (record.type) {
+    case recordTypes.ResourceSendRequest:
+        // Make resource receive record last since request was sent; make finish record last since response received.
+        presentationModel._sendRequestRecords[record.data["requestId"]] = this;
+        break;
+
+    case recordTypes.ScheduleResourceRequest:
+        presentationModel._scheduledResourceRequests[record.data["url"]] = this;
+        break;
+
+    case recordTypes.ResourceReceiveResponse:
+        var sendRequestRecord = presentationModel._sendRequestRecords[record.data["requestId"]];
+        if (sendRequestRecord) { // False if we started instrumentation in the middle of request.
+            this.url = sendRequestRecord.url;
+            // Now that we have resource in the collection, recalculate details in order to display short url.
+            sendRequestRecord._refreshDetails();
+            if (sendRequestRecord.parent !== presentationModel._rootRecord && sendRequestRecord.parent.type === recordTypes.ScheduleResourceRequest)
+                sendRequestRecord.parent._refreshDetails();
+        }
+        break;
+
+    case recordTypes.ResourceReceivedData:
+    case recordTypes.ResourceFinish:
+        var sendRequestRecord = presentationModel._sendRequestRecords[record.data["requestId"]];
+        if (sendRequestRecord) // False for main resource.
+            this.url = sendRequestRecord.url;
+        break;
+
+    case recordTypes.TimerInstall:
+        this.timeout = record.data["timeout"];
+        this.singleShot = record.data["singleShot"];
+        presentationModel._timerRecords[record.data["timerId"]] = this;
+        break;
+
+    case recordTypes.TimerFire:
+        var timerInstalledRecord = presentationModel._timerRecords[record.data["timerId"]];
+        if (timerInstalledRecord) {
+            this.callSiteStackTrace = timerInstalledRecord.stackTrace;
+            this.timeout = timerInstalledRecord.timeout;
+            this.singleShot = timerInstalledRecord.singleShot;
+        }
+        break;
+
+    case recordTypes.RequestAnimationFrame:
+        presentationModel._requestAnimationFrameRecords[record.data["id"]] = this;
+        break;
+
+    case recordTypes.FireAnimationFrame:
+        var requestAnimationRecord = presentationModel._requestAnimationFrameRecords[record.data["id"]];
+        if (requestAnimationRecord)
+            this.callSiteStackTrace = requestAnimationRecord.stackTrace;
+        break;
+
+    case recordTypes.Time:
+        var message = record.data["message"];
+        var oldReference = presentationModel._timeRecords[message];
+        if (oldReference)
+            break;
+        presentationModel._timeRecords[message] = this;
+        if (origin)
+            presentationModel._timeRecordStack.push(this);
+        break;
+
+    case recordTypes.TimeEnd:
+        var message = record.data["message"];
+        var timeRecord = presentationModel._timeRecords[message];
+        delete presentationModel._timeRecords[message];
+        if (timeRecord) {
+            this.timeRecord = timeRecord;
+            timeRecord.timeEndRecord = this;
+            var intervalDuration = this.startTime - timeRecord.startTime;
+            this.intervalDuration = intervalDuration;
+            timeRecord.intervalDuration = intervalDuration;
+            if (!origin)
+                break;
+            var recordStack = presentationModel._timeRecordStack;
+            recordStack.splice(recordStack.indexOf(timeRecord), 1);
+            for (var index = recordStack.length; index; --index) {
+                var openRecord = recordStack[index - 1];
+                if (openRecord.startTime > timeRecord.startTime)
+                    continue;
+                WebInspector.TimelinePresentationModel.adoptRecord(openRecord, timeRecord);
+                break;
+            }
+        }
+        break;
+
+    case recordTypes.ScheduleStyleRecalculation:
+        presentationModel._lastScheduleStyleRecalculation[this.frameId] = this;
+        break;
+
+    case recordTypes.RecalculateStyles:
+        var scheduleStyleRecalculationRecord = presentationModel._lastScheduleStyleRecalculation[this.frameId];
+        if (!scheduleStyleRecalculationRecord)
+            break;
+        this.callSiteStackTrace = scheduleStyleRecalculationRecord.stackTrace;
+        break;
+
+    case recordTypes.InvalidateLayout:
+        // Consider style recalculation as a reason for layout invalidation,
+        // but only if we had no earlier layout invalidation records.
+        var styleRecalcStack;
+        if (!presentationModel._layoutInvalidateStack[this.frameId]) {
+            for (var outerRecord = parentRecord; outerRecord; outerRecord = record.parent) {
+                if (outerRecord.type === recordTypes.RecalculateStyles) {
+                    styleRecalcStack = outerRecord.callSiteStackTrace;
+                    break;
+                }
+            }
+        }
+        presentationModel._layoutInvalidateStack[this.frameId] = styleRecalcStack || this.stackTrace;
+        break;
+
+    case recordTypes.Layout:
+        var layoutInvalidateStack = presentationModel._layoutInvalidateStack[this.frameId];
+        if (layoutInvalidateStack)
+            this.callSiteStackTrace = layoutInvalidateStack;
+        if (this.stackTrace)
+            this.setHasWarning();
+        presentationModel._layoutInvalidateStack[this.frameId] = null;
+        this.highlightQuad = record.data.root || WebInspector.TimelinePresentationModel.quadFromRectData(record.data);
+        break;
+
+    case recordTypes.Paint:
+        this.highlightQuad = record.data.clip || WebInspector.TimelinePresentationModel.quadFromRectData(record.data);
+        break;
+
+    case recordTypes.WebSocketCreate:
+        this.webSocketURL = record.data["url"];
+        if (typeof record.data["webSocketProtocol"] !== "undefined")
+            this.webSocketProtocol = record.data["webSocketProtocol"];
+        presentationModel._webSocketCreateRecords[record.data["identifier"]] = this;
+        break;
+   
+    case recordTypes.WebSocketSendHandshakeRequest:
+    case recordTypes.WebSocketReceiveHandshakeResponse:
+    case recordTypes.WebSocketDestroy:
+        var webSocketCreateRecord = presentationModel._webSocketCreateRecords[record.data["identifier"]];
+        if (webSocketCreateRecord) { // False if we started instrumentation in the middle of request.
+            this.webSocketURL = webSocketCreateRecord.webSocketURL;
+            if (typeof webSocketCreateRecord.webSocketProtocol !== "undefined")
+                this.webSocketProtocol = webSocketCreateRecord.webSocketProtocol;
+        }
+        break;
+    }
+}
+
+WebInspector.TimelinePresentationModel.adoptRecord = function(newParent, record)
+{
+    record.parent.children.splice(record.parent.children.indexOf(record));
+    WebInspector.TimelinePresentationModel.insertRetrospectiveRecord(newParent, record);
+    record.parent = newParent;
+}
+
+WebInspector.TimelinePresentationModel.insertRetrospectiveRecord = function(parent, record)
+{
+    function compareStartTime(value, record)
+    {
+        return value < record.startTime ? -1 : 1;
+    }
+    
+    parent.children.splice(insertionIndexForObjectInListSortedByFunction(record.startTime, parent.children, compareStartTime), 0, record);
+}
+
+WebInspector.TimelinePresentationModel.Record.prototype = {
+    get lastChildEndTime()
+    {
+        return this._lastChildEndTime;
+    },
+
+    set lastChildEndTime(time)
+    {
+        this._lastChildEndTime = time;
+    },
+
+    get selfTime()
+    {
+        return this.coalesced ? this._lastChildEndTime - this.startTime : this._selfTime;
+    },
+
+    set selfTime(time)
+    {
+        this._selfTime = time;
+    },
+
+    get cpuTime()
+    {
+        return this._cpuTime;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isRoot: function()
+    {
+        return this.type === WebInspector.TimelineModel.RecordType.Root;
+    },
+
+    /**
+     * @return {WebInspector.TimelinePresentationModel.Record}
+     */
+    origin: function()
+    {
+        return this._origin || this.parent;
+    },
+
+    /**
+     * @return {Array.<WebInspector.TimelinePresentationModel.Record>}
+     */
+    get children()
+    {
+        return this._children;
+    },
+
+    /**
+     * @return {number}
+     */
+    get visibleChildrenCount()
+    {
+        return this._visibleChildrenCount || 0;
+    },
+
+    /**
+     * @return {number}
+     */
+    get invisibleChildrenCount()
+    {
+        return this._invisibleChildrenCount || 0;
+    },
+
+    /**
+     * @return {WebInspector.TimelineCategory}
+     */
+    get category()
+    {
+        return WebInspector.TimelinePresentationModel.recordStyle(this._record).category
+    },
+
+    /**
+     * @return {string}
+     */
+    get title()
+    {
+        return this.type === WebInspector.TimelineModel.RecordType.TimeStamp ? this._record.data["message"] :
+            WebInspector.TimelinePresentationModel.recordStyle(this._record).title;
+    },
+
+    /**
+     * @return {number}
+     */
+    get startTime()
+    {
+        return WebInspector.TimelineModel.startTimeInSeconds(this._record);
+    },
+
+    /**
+     * @return {number}
+     */
+    get endTime()
+    {
+        return WebInspector.TimelineModel.endTimeInSeconds(this._record);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    get isBackground()
+    {
+        return !!this._record.thread;
+    },
+
+    /**
+     * @return {Object}
+     */
+    get data()
+    {
+        return this._record.data;
+    },
+
+    /**
+     * @return {string}
+     */
+    get type()
+    {
+        return this._record.type;
+    },
+
+    /**
+     * @return {string}
+     */
+    get frameId()
+    {
+        return this._record.frameId;
+    },
+
+    /**
+     * @return {number}
+     */
+    get usedHeapSizeDelta()
+    {
+        return this._record.usedHeapSizeDelta || 0;
+    },
+
+    /**
+     * @return {number}
+     */
+    get usedHeapSize()
+    {
+        return this._record.usedHeapSize;
+    },
+
+    /**
+     * @return {Array.<DebuggerAgent.CallFrame>?}
+     */
+    get stackTrace()
+    {
+        if (this._record.stackTrace && this._record.stackTrace.length)
+            return this._record.stackTrace;
+        return null;
+    },
+
+    containsTime: function(time)
+    {
+        return this.startTime <= time && time <= this.endTime;
+    },
+
+    /**
+     * @param {function(Element)} callback
+     */
+    generatePopupContent: function(callback)
+    {
+        if (WebInspector.TimelinePresentationModel.needsPreviewElement(this.type))
+            WebInspector.DOMPresentationUtils.buildImagePreviewContents(this.url, false, this._generatePopupContentWithImagePreview.bind(this, callback));
+        else
+            this._generatePopupContentWithImagePreview(callback);
+    },
+
+    /**
+     * @param {function(Element)} callback
+     * @param {Element=} previewElement
+     */
+    _generatePopupContentWithImagePreview: function(callback, previewElement)
+    {
+        var contentHelper = new WebInspector.PopoverContentHelper(this.title);
+        var text = WebInspector.UIString("%s (at %s)", Number.secondsToString(this._lastChildEndTime - this.startTime, true),
+            Number.secondsToString(this._startTimeOffset));
+        contentHelper.appendTextRow(WebInspector.UIString("Duration"), text);
+
+        if (this._children.length) {
+            if (!this.coalesced)
+                contentHelper.appendTextRow(WebInspector.UIString("Self Time"), Number.secondsToString(this._selfTime, true));
+            contentHelper.appendTextRow(WebInspector.UIString("CPU Time"), Number.secondsToString(this._cpuTime, true));
+            contentHelper.appendElementRow(WebInspector.UIString("Aggregated Time"),
+                WebInspector.TimelinePresentationModel._generateAggregatedInfo(this._aggregatedStats));
+        }
+
+        if (this.coalesced) {
+            callback(contentHelper.contentTable());
+            return;
+        }
+        const recordTypes = WebInspector.TimelineModel.RecordType;
+
+        // The messages may vary per record type;
+        var callSiteStackTraceLabel;
+        var callStackLabel;
+
+        switch (this.type) {
+            case recordTypes.GCEvent:
+                contentHelper.appendTextRow(WebInspector.UIString("Collected"), Number.bytesToString(this.data["usedHeapSizeDelta"]));
+                break;
+            case recordTypes.TimerFire:
+                callSiteStackTraceLabel = WebInspector.UIString("Timer installed");
+                // Fall-through intended.
+
+            case recordTypes.TimerInstall:
+            case recordTypes.TimerRemove:
+                contentHelper.appendTextRow(WebInspector.UIString("Timer ID"), this.data["timerId"]);
+                if (typeof this.timeout === "number") {
+                    contentHelper.appendTextRow(WebInspector.UIString("Timeout"), Number.secondsToString(this.timeout / 1000));
+                    contentHelper.appendTextRow(WebInspector.UIString("Repeats"), !this.singleShot);
+                }
+                break;
+            case recordTypes.FireAnimationFrame:
+                callSiteStackTraceLabel = WebInspector.UIString("Animation frame requested");
+                contentHelper.appendTextRow(WebInspector.UIString("Callback ID"), this.data["id"]);
+                break;
+            case recordTypes.FunctionCall:
+                contentHelper.appendElementRow(WebInspector.UIString("Location"), this._linkifyScriptLocation());
+                break;
+            case recordTypes.ScheduleResourceRequest:
+            case recordTypes.ResourceSendRequest:
+            case recordTypes.ResourceReceiveResponse:
+            case recordTypes.ResourceReceivedData:
+            case recordTypes.ResourceFinish:
+                contentHelper.appendElementRow(WebInspector.UIString("Resource"), WebInspector.linkifyResourceAsNode(this.url));
+                if (previewElement)
+                    contentHelper.appendElementRow(WebInspector.UIString("Preview"), previewElement);
+                if (this.data["requestMethod"])
+                    contentHelper.appendTextRow(WebInspector.UIString("Request Method"), this.data["requestMethod"]);
+                if (typeof this.data["statusCode"] === "number")
+                    contentHelper.appendTextRow(WebInspector.UIString("Status Code"), this.data["statusCode"]);
+                if (this.data["mimeType"])
+                    contentHelper.appendTextRow(WebInspector.UIString("MIME Type"), this.data["mimeType"]);
+                if (this.data["encodedDataLength"])
+                    contentHelper.appendTextRow(WebInspector.UIString("Encoded Data Length"), WebInspector.UIString("%d Bytes", this.data["encodedDataLength"]));
+                break;
+            case recordTypes.EvaluateScript:
+                if (this.data && this.url)
+                    contentHelper.appendElementRow(WebInspector.UIString("Script"), this._linkifyLocation(this.url, this.data["lineNumber"]));
+                break;
+            case recordTypes.Paint:
+                var clip = this.data["clip"];
+                if (clip) {
+                    contentHelper.appendTextRow(WebInspector.UIString("Location"), WebInspector.UIString("(%d, %d)", clip[0], clip[1]));
+                    var clipWidth = WebInspector.TimelinePresentationModel.quadWidth(clip);
+                    var clipHeight = WebInspector.TimelinePresentationModel.quadHeight(clip);
+                    contentHelper.appendTextRow(WebInspector.UIString("Dimensions"), WebInspector.UIString("%d × %d", clipWidth, clipHeight));
+                } else {
+                    // Backward compatibility: older version used x, y, width, height fields directly in data.
+                    if (typeof this.data["x"] !== "undefined" && typeof this.data["y"] !== "undefined")
+                        contentHelper.appendTextRow(WebInspector.UIString("Location"), WebInspector.UIString("(%d, %d)", this.data["x"], this.data["y"]));
+                    if (typeof this.data["width"] !== "undefined" && typeof this.data["height"] !== "undefined")
+                        contentHelper.appendTextRow(WebInspector.UIString("Dimensions"), WebInspector.UIString("%d\u2009\u00d7\u2009%d", this.data["width"], this.data["height"]));
+                }
+                break;
+            case recordTypes.RecalculateStyles: // We don't want to see default details.
+                if (this.data["elementCount"])
+                    contentHelper.appendTextRow(WebInspector.UIString("Elements affected"), this.data["elementCount"]);
+                callStackLabel = WebInspector.UIString("Styles recalculation forced");
+                break;
+            case recordTypes.Layout:
+                if (this.data["dirtyObjects"])
+                    contentHelper.appendTextRow(WebInspector.UIString("Nodes that need layout"), this.data["dirtyObjects"]);
+                if (this.data["totalObjects"])
+                    contentHelper.appendTextRow(WebInspector.UIString("Layout tree size"), this.data["totalObjects"]);
+                if (typeof this.data["partialLayout"] === "boolean") {
+                    contentHelper.appendTextRow(WebInspector.UIString("Layout scope"),
+                       this.data["partialLayout"] ? WebInspector.UIString("Partial") : WebInspector.UIString("Whole document"));
+                }
+                callSiteStackTraceLabel = WebInspector.UIString("Layout invalidated");
+                if (this.stackTrace) {
+                    callStackLabel = WebInspector.UIString("Layout forced");
+                    contentHelper.appendTextRow(WebInspector.UIString("Note"), WebInspector.UIString("Forced synchronous layout is a possible performance bottleneck."));
+                }
+                break;
+            case recordTypes.Time:
+            case recordTypes.TimeEnd:
+                contentHelper.appendTextRow(WebInspector.UIString("Message"), this.data["message"]);
+                if (typeof this.intervalDuration === "number")
+                    contentHelper.appendTextRow(WebInspector.UIString("Interval Duration"), Number.secondsToString(this.intervalDuration, true));
+                break;
+            case recordTypes.WebSocketCreate:
+            case recordTypes.WebSocketSendHandshakeRequest:
+            case recordTypes.WebSocketReceiveHandshakeResponse:
+            case recordTypes.WebSocketDestroy:
+                if (typeof this.webSocketURL !== "undefined")
+                    contentHelper.appendTextRow(WebInspector.UIString("URL"), this.webSocketURL);
+                if (typeof this.webSocketProtocol !== "undefined")
+                    contentHelper.appendTextRow(WebInspector.UIString("WebSocket Protocol"), this.webSocketProtocol);
+                if (typeof this.data["message"] !== "undefined")
+                    contentHelper.appendTextRow(WebInspector.UIString("Message"), this.data["message"])
+                    break;
+            default:
+                if (this.detailsNode())
+                    contentHelper.appendElementRow(WebInspector.UIString("Details"), this.detailsNode().childNodes[1].cloneNode());
+                break;
+        }
+
+        if (this.scriptName && this.type !== recordTypes.FunctionCall)
+            contentHelper.appendElementRow(WebInspector.UIString("Function Call"), this._linkifyScriptLocation());
+
+        if (this.usedHeapSize) {
+            if (this.usedHeapSizeDelta) {
+                var sign = this.usedHeapSizeDelta > 0 ? "+" : "-";
+                contentHelper.appendTextRow(WebInspector.UIString("Used Heap Size"),
+                    WebInspector.UIString("%s (%s%s)", Number.bytesToString(this.usedHeapSize), sign, Number.bytesToString(this.usedHeapSizeDelta)));
+            } else if (this.category === WebInspector.TimelinePresentationModel.categories().scripting)
+                contentHelper.appendTextRow(WebInspector.UIString("Used Heap Size"), Number.bytesToString(this.usedHeapSize));
+        }
+
+        if (this.callSiteStackTrace)
+            contentHelper.appendStackTrace(callSiteStackTraceLabel || WebInspector.UIString("Call Site stack"), this.callSiteStackTrace, this._linkifyCallFrame.bind(this));
+
+        if (this.stackTrace)
+            contentHelper.appendStackTrace(callStackLabel || WebInspector.UIString("Call Stack"), this.stackTrace, this._linkifyCallFrame.bind(this));
+
+        callback(contentHelper.contentTable());
+    },
+
+    _refreshDetails: function()
+    {
+        delete this._detailsNode;
+    },
+
+    /**
+     * @return {?Node}
+     */
+    detailsNode: function()
+    {
+        if (typeof this._detailsNode === "undefined") {
+            this._detailsNode = this._getRecordDetails();
+
+            if (this._detailsNode && !this.coalesced) {
+                this._detailsNode.insertBefore(document.createTextNode("("), this._detailsNode.firstChild);
+                this._detailsNode.appendChild(document.createTextNode(")"));
+            }
+        }
+        return this._detailsNode;
+    },
+
+    _createSpanWithText: function(textContent)
+    {
+        var node = document.createElement("span");
+        node.textContent = textContent;
+        return node;
+    },
+
+    /**
+     * @return {?Node}
+     */
+    _getRecordDetails: function()
+    {
+        var details;
+        if (this.coalesced)
+            return this._createSpanWithText(WebInspector.UIString("× %d", this.children.length));
+
+        switch (this.type) {
+        case WebInspector.TimelineModel.RecordType.GCEvent:
+            details = WebInspector.UIString("%s collected", Number.bytesToString(this.data["usedHeapSizeDelta"]));
+            break;
+        case WebInspector.TimelineModel.RecordType.TimerFire:
+            details = this._linkifyScriptLocation(this.data["timerId"]);
+            break;
+        case WebInspector.TimelineModel.RecordType.FunctionCall:
+            details = this._linkifyScriptLocation();
+            break;
+        case WebInspector.TimelineModel.RecordType.FireAnimationFrame:
+            details = this._linkifyScriptLocation(this.data["id"]);
+            break;
+        case WebInspector.TimelineModel.RecordType.EventDispatch:
+            details = this.data ? this.data["type"] : null;
+            break;
+        case WebInspector.TimelineModel.RecordType.Paint:
+            var width = this.data.clip ? WebInspector.TimelinePresentationModel.quadWidth(this.data.clip) : this.data.width;
+            var height = this.data.clip ? WebInspector.TimelinePresentationModel.quadHeight(this.data.clip) : this.data.height;
+            if (width && height)
+                details = WebInspector.UIString("%d\u2009\u00d7\u2009%d", width, height);
+            break;
+        case WebInspector.TimelineModel.RecordType.DecodeImage:
+            details = this.data["imageType"];
+            break;
+        case WebInspector.TimelineModel.RecordType.ResizeImage:
+            details = this.data["cached"] ? WebInspector.UIString("cached") : WebInspector.UIString("non-cached");
+            break;
+        case WebInspector.TimelineModel.RecordType.TimerInstall:
+        case WebInspector.TimelineModel.RecordType.TimerRemove:
+            details = this._linkifyTopCallFrame(this.data["timerId"]);
+            break;
+        case WebInspector.TimelineModel.RecordType.RequestAnimationFrame:
+        case WebInspector.TimelineModel.RecordType.CancelAnimationFrame:
+            details = this._linkifyTopCallFrame(this.data["id"]);
+            break;
+        case WebInspector.TimelineModel.RecordType.ParseHTML:
+        case WebInspector.TimelineModel.RecordType.RecalculateStyles:
+            details = this._linkifyTopCallFrame();
+            break;
+        case WebInspector.TimelineModel.RecordType.EvaluateScript:
+            details = this.url ? this._linkifyLocation(this.url, this.data["lineNumber"], 0) : null;
+            break;
+        case WebInspector.TimelineModel.RecordType.XHRReadyStateChange:
+        case WebInspector.TimelineModel.RecordType.XHRLoad:
+        case WebInspector.TimelineModel.RecordType.ScheduleResourceRequest:
+        case WebInspector.TimelineModel.RecordType.ResourceSendRequest:
+        case WebInspector.TimelineModel.RecordType.ResourceReceivedData:
+        case WebInspector.TimelineModel.RecordType.ResourceReceiveResponse:
+        case WebInspector.TimelineModel.RecordType.ResourceFinish:
+            details = WebInspector.displayNameForURL(this.url);
+            break;
+        case WebInspector.TimelineModel.RecordType.Time:
+        case WebInspector.TimelineModel.RecordType.TimeEnd:
+        case WebInspector.TimelineModel.RecordType.TimeStamp:
+            details = this.data["message"];
+            break;
+        default:
+            details = this._linkifyScriptLocation() || this._linkifyTopCallFrame() || null;
+            break;
+        }
+
+        if (details) {
+            if (details instanceof Node)
+                details.tabIndex = -1;
+            else
+                return this._createSpanWithText("" + details);
+        }
+
+        return details || null;
+    },
+
+    /**
+     * @param {string} url
+     * @param {number} lineNumber
+     * @param {number=} columnNumber
+     */
+    _linkifyLocation: function(url, lineNumber, columnNumber)
+    {
+        // FIXME(62725): stack trace line/column numbers are one-based.
+        columnNumber = columnNumber ? columnNumber - 1 : 0;
+        return this._linkifier.linkifyLocation(url, lineNumber - 1, columnNumber, "timeline-details");
+    },
+
+    _linkifyCallFrame: function(callFrame)
+    {
+        return this._linkifyLocation(callFrame.url, callFrame.lineNumber, callFrame.columnNumber);
+    },
+
+    /**
+     * @param {string=} defaultValue
+     */
+    _linkifyTopCallFrame: function(defaultValue)
+    {
+        if (this.stackTrace)
+            return this._linkifyCallFrame(this.stackTrace[0]);
+        if (this.callSiteStackTrace)
+            return this._linkifyCallFrame(this.callSiteStackTrace[0]);
+        return defaultValue;
+    },
+
+    /**
+     * @param {*=} defaultValue
+     * @return {Element|string}
+     */
+    _linkifyScriptLocation: function(defaultValue)
+    {
+        if (this.scriptName)
+            return this._linkifyLocation(this.scriptName, this.scriptLine, 0);
+        else
+            return defaultValue ? "" + defaultValue : null;
+    },
+
+    calculateAggregatedStats: function()
+    {
+        this._aggregatedStats = {};
+        this._cpuTime = this._selfTime;
+
+        for (var index = this._children.length; index; --index) {
+            var child = this._children[index - 1];
+            for (var category in child._aggregatedStats)
+                this._aggregatedStats[category] = (this._aggregatedStats[category] || 0) + child._aggregatedStats[category];
+        }
+        for (var category in this._aggregatedStats)
+            this._cpuTime += this._aggregatedStats[category];
+        this._aggregatedStats[this.category.name] = (this._aggregatedStats[this.category.name] || 0) + this._selfTime;
+    },
+
+    get aggregatedStats()
+    {
+        return this._aggregatedStats;
+    },
+
+    setHasWarning: function()
+    {
+        this.hasWarning = true;
+        for (var parent = this.parent; parent && !parent.childHasWarning; parent = parent.parent)
+            parent.childHasWarning = true;
+    }
+}
+
+/**
+ * @param {Object} aggregatedStats
+ */
+WebInspector.TimelinePresentationModel._generateAggregatedInfo = function(aggregatedStats)
+{
+    var cell = document.createElement("span");
+    cell.className = "timeline-aggregated-info";
+    for (var index in aggregatedStats) {
+        var label = document.createElement("div");
+        label.className = "timeline-aggregated-category timeline-" + index;
+        cell.appendChild(label);
+        var text = document.createElement("span");
+        text.textContent = Number.secondsToString(aggregatedStats[index], true);
+        cell.appendChild(text);
+    }
+    return cell;
+}
+
+WebInspector.TimelinePresentationModel.generatePopupContentForFrame = function(frame)
+{
+    var contentHelper = new WebInspector.PopoverContentHelper(WebInspector.UIString("Frame"));
+    var durationInSeconds = frame.endTime - frame.startTime;
+    var durationText = WebInspector.UIString("%s (at %s)", Number.secondsToString(frame.endTime - frame.startTime, true),
+        Number.secondsToString(frame.startTimeOffset, true));
+    contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText);
+    contentHelper.appendTextRow(WebInspector.UIString("FPS"), Math.floor(1 / durationInSeconds));
+    contentHelper.appendTextRow(WebInspector.UIString("CPU time"), Number.secondsToString(frame.cpuTime, true));
+    contentHelper.appendElementRow(WebInspector.UIString("Aggregated Time"),
+        WebInspector.TimelinePresentationModel._generateAggregatedInfo(frame.timeByCategory));
+
+    return contentHelper.contentTable();
+}
+
+/**
+ * @param {WebInspector.FrameStatistics} statistics
+ */
+WebInspector.TimelinePresentationModel.generatePopupContentForFrameStatistics = function(statistics)
+{
+    /**
+     * @param {number} time
+     */
+    function formatTimeAndFPS(time)
+    {
+        return WebInspector.UIString("%s (%.0f FPS)", Number.secondsToString(time, true), 1 / time);
+    }
+
+    var contentHelper = new WebInspector.PopoverContentHelper(WebInspector.UIString("Selected Range"));
+
+    contentHelper.appendTextRow(WebInspector.UIString("Selected range"), WebInspector.UIString("%s\u2013%s (%d frames)",
+        Number.secondsToString(statistics.startOffset, true), Number.secondsToString(statistics.endOffset, true), statistics.frameCount));
+    contentHelper.appendTextRow(WebInspector.UIString("Minimum Time"), formatTimeAndFPS(statistics.minDuration));
+    contentHelper.appendTextRow(WebInspector.UIString("Average Time"), formatTimeAndFPS(statistics.average));
+    contentHelper.appendTextRow(WebInspector.UIString("Maximum Time"), formatTimeAndFPS(statistics.maxDuration));
+    contentHelper.appendTextRow(WebInspector.UIString("Standard Deviation"), Number.secondsToString(statistics.stddev, true));
+    contentHelper.appendElementRow(WebInspector.UIString("Time by category"),
+        WebInspector.TimelinePresentationModel._generateAggregatedInfo(statistics.timeByCategory));
+
+    return contentHelper.contentTable();
+}
+
+/**
+ * @param {CanvasRenderingContext2D} context
+ * @param {number} width
+ * @param {number} height
+ * @param {string} color0
+ * @param {string} color1
+ * @param {string} color2
+ */
+WebInspector.TimelinePresentationModel.createFillStyle = function(context, width, height, color0, color1, color2)
+{
+    var gradient = context.createLinearGradient(0, 0, width, height);
+    gradient.addColorStop(0, color0);
+    gradient.addColorStop(0.25, color1);
+    gradient.addColorStop(0.75, color1);
+    gradient.addColorStop(1, color2);
+    return gradient;
+}
+
+/**
+ * @param {CanvasRenderingContext2D} context
+ * @param {number} width
+ * @param {number} height
+ * @param {WebInspector.TimelineCategory} category
+ */
+WebInspector.TimelinePresentationModel.createFillStyleForCategory = function(context, width, height, category)
+{
+    return WebInspector.TimelinePresentationModel.createFillStyle(context, width, height, category.fillColorStop0, category.fillColorStop1, category.borderColor);
+}
+
+/**
+ * @param {WebInspector.TimelineCategory} category
+ */
+WebInspector.TimelinePresentationModel.createStyleRuleForCategory = function(category)
+{
+    var selector = ".timeline-category-" + category.name + " .timeline-graph-bar, " +
+        ".timeline-category-statusbar-item.timeline-category-" + category.name + " .timeline-category-checkbox, " +
+        ".popover .timeline-" + category.name + ", " +
+        ".timeline-category-" + category.name + " .timeline-tree-icon"
+
+    return selector + " { background-image: -webkit-linear-gradient(" +
+       category.fillColorStop0 + ", " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + " 25%, " + category.fillColorStop1 + ");" +
+       " border-color: " + category.borderColor +
+       "}";
+}
+
+/**
+ * @param {Array.<number>} quad
+ * @return {number}
+ */
+WebInspector.TimelinePresentationModel.quadWidth = function(quad)
+{
+    return Math.round(Math.sqrt(Math.pow(quad[0] - quad[2], 2) + Math.pow(quad[1] - quad[3], 2)));
+}
+
+/**
+ * @param {Array.<number>} quad
+ * @return {number}
+ */
+WebInspector.TimelinePresentationModel.quadHeight = function(quad)
+{
+    return Math.round(Math.sqrt(Math.pow(quad[0] - quad[6], 2) + Math.pow(quad[1] - quad[7], 2)));
+}
+
+/**
+ * @param {Object} data
+ * @return {Array.<number>?}
+ */
+WebInspector.TimelinePresentationModel.quadFromRectData = function(data)
+{
+    if (typeof data["x"] === "undefined" || typeof data["y"] === "undefined")
+        return null;
+    var x0 = data["x"];
+    var x1 = data["x"] + data["width"];
+    var y0 = data["y"];
+    var y1 = data["y"] + data["height"];
+    return [x0, y0, x1, y0, x1, y1, x0, y1];
+}
+
+/**
+ * @interface
+ */
+WebInspector.TimelinePresentationModel.Filter = function()
+{
+}
+
+WebInspector.TimelinePresentationModel.Filter.prototype = {
+    /**
+     * @param {!WebInspector.TimelinePresentationModel.Record} record
+     * @return {boolean}
+     */
+    accept: function(record) { return false; }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @param {string} name
+ * @param {string} title
+ * @param {number} overviewStripGroupIndex
+ * @param {string} borderColor
+ * @param {string} fillColorStop0
+ * @param {string} fillColorStop1
+ */
+WebInspector.TimelineCategory = function(name, title, overviewStripGroupIndex, borderColor, fillColorStop0, fillColorStop1)
+{
+    this.name = name;
+    this.title = title;
+    this.overviewStripGroupIndex = overviewStripGroupIndex;
+    this.borderColor = borderColor;
+    this.fillColorStop0 = fillColorStop0;
+    this.fillColorStop1 = fillColorStop1;
+    this.hidden = false;
+}
+
+WebInspector.TimelineCategory.Events = {
+    VisibilityChanged: "VisibilityChanged"
+};
+
+WebInspector.TimelineCategory.prototype = {
+    /**
+     * @return {boolean}
+     */
+    get hidden()
+    {
+        return this._hidden;
+    },
+
+    set hidden(hidden)
+    {
+        this._hidden = hidden;
+        this.dispatchEventToListeners(WebInspector.TimelineCategory.Events.VisibilityChanged, this);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
diff --git a/Source/devtools/front_end/Toolbar.js b/Source/devtools/front_end/Toolbar.js
new file mode 100644
index 0000000..67acb74
--- /dev/null
+++ b/Source/devtools/front_end/Toolbar.js
@@ -0,0 +1,522 @@
+ /*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
+ * Copyright (C) 2009 Joseph Pecoraro
+ * 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.Toolbar = function()
+{
+    this.element = document.getElementById("toolbar");
+    WebInspector.installDragHandle(this.element, this._toolbarDragStart.bind(this), this._toolbarDrag.bind(this), this._toolbarDragEnd.bind(this), "default");
+
+    this._dropdownButton = document.getElementById("toolbar-dropdown-arrow");
+    this._dropdownButton.addEventListener("click", this._toggleDropdown.bind(this), false);
+
+    this._panelsMenuButton = document.getElementById("toolbar-panels-menu");
+    if (this._isToolbarCustomizable()) {
+        this._panelsMenuButton.addEventListener("mousedown", this._togglePanelsMenu.bind(this), false);
+        this._panelsMenuButton.removeStyleClass("hidden");
+    }
+
+    document.getElementById("close-button-left").addEventListener("click", this._onClose, true);
+    document.getElementById("close-button-right").addEventListener("click", this._onClose, true);
+
+    this._panelDescriptors = [];
+}
+
+WebInspector.Toolbar.prototype = {
+    resize: function()
+    {
+        this._updateDropdownButtonAndHideDropdown();
+    },
+
+    /**
+     * @param {!WebInspector.PanelDescriptor} panelDescriptor
+     */
+    addPanel: function(panelDescriptor)
+    {
+        this._panelDescriptors.push(panelDescriptor);
+        panelDescriptor._toolbarElement = this._createPanelToolbarItem(panelDescriptor);
+        if (!this._isToolbarCustomizable() || this._isPanelVisible(panelDescriptor.name()))
+            this.element.insertBefore(panelDescriptor._toolbarElement, this._panelInsertLocation(panelDescriptor));
+        this._updatePanelsMenuState();
+        this.resize();
+    },
+
+    /**
+     * @param {!WebInspector.PanelDescriptor} panelDescriptor
+     * @return {Element}
+     */
+    _panelInsertLocation: function(panelDescriptor)
+    {
+        if (!this._isToolbarCustomizable())
+            return null;
+
+        if (this._isDefaultPanel(panelDescriptor.name()))
+            return this._firstNonDefaultPanel || null;
+
+        if (!this._firstNonDefaultPanel)
+            this._firstNonDefaultPanel = panelDescriptor._toolbarElement;
+        return null;
+    },
+
+    /**
+     * @param {!string} name
+     * @return {boolean}
+     */
+    _isDefaultPanel: function(name)
+    {
+        var defaultPanels = {
+            "elements": true,
+            "resources": true,
+            "scripts": true,
+            "console": true,
+            "network": true,
+            "timeline": true,
+        };
+        return !!defaultPanels[name];
+    },
+
+    /**
+     * @param {!string} name
+     * @return {boolean}
+     */
+    _isPanelVisibleByDefault: function(name)
+    {
+        var visible = {
+            "elements": true,
+            "console": true,
+            "network": true,
+            "scripts": true,
+            "timeline": true,
+            "profiles": true,
+            "cpu-profiler": true,
+            "heap-profiler": true,
+            "audits": true,
+            "resources": true,
+        };
+        return !!visible[name];
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _isToolbarCustomizable: function()
+    {
+        return WebInspector.experimentsSettings.customizableToolbar.isEnabled();
+    },
+
+    /**
+     * @param {!string} name
+     * @return {boolean}
+     */
+    _isPanelVisible: function(name)
+    {
+        if (!this._isToolbarCustomizable())
+            return true;
+        var visiblePanels = WebInspector.settings.visiblePanels.get();
+        return visiblePanels.hasOwnProperty(name) ? visiblePanels[name] : this._isPanelVisibleByDefault(name);
+    },
+
+    /**
+     * @param {!string} name
+     * @param {boolean} visible
+     */
+    _setPanelVisible: function(name, visible)
+    {
+        var visiblePanels = WebInspector.settings.visiblePanels.get();
+        visiblePanels[name] = visible;
+        WebInspector.settings.visiblePanels.set(visiblePanels);
+    },
+
+    /**
+     * @param {!WebInspector.PanelDescriptor} panelDescriptor
+     */
+    _hidePanel: function(panelDescriptor)
+    {
+        if (!this._isPanelVisible(panelDescriptor.name()))
+            return;
+        var switchToSibling = panelDescriptor._toolbarElement.nextSibling;
+        if (!switchToSibling || !switchToSibling.classList.contains("toggleable"))
+            switchToSibling = panelDescriptor._toolbarElement.previousSibling;
+        if (!switchToSibling || !switchToSibling.classList || !switchToSibling.classList.contains("toggleable"))
+            return;
+        this._setPanelVisible(panelDescriptor.name(), false);
+        this.element.removeChild(panelDescriptor._toolbarElement);
+        if (WebInspector.inspectorView.currentPanel().name === panelDescriptor.name()) {
+            for (var i = 0; i < this._panelDescriptors.length; ++i) {
+                var descr = this._panelDescriptors[i];
+                if (descr._toolbarElement === switchToSibling) {
+                    WebInspector.showPanel(descr.name());
+                    break;
+                }
+            }
+        }
+        this._updatePanelsMenuState();
+        this.resize();
+    },
+
+    _updatePanelsMenuState: function()
+    {
+        if (this._panelDescriptors.every(function (descr) { return this._isPanelVisible(descr.name()); }, this) && this._allItemsFitOntoToolbar())
+            document.getElementById("toolbar-panels-menu").addStyleClass("disabled");
+        else
+            document.getElementById("toolbar-panels-menu").removeStyleClass("disabled");
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _allItemsFitOntoToolbar: function()
+    {
+        var toolbarItems = this.element.querySelectorAll(".toolbar-item.toggleable");
+        return toolbarItems.length === 0 || this.element.scrollHeight < toolbarItems[0].offsetHeight * 2;
+    },
+
+    /**
+     * @param {!WebInspector.PanelDescriptor} panelDescriptor
+     */
+    _showPanel: function(panelDescriptor)
+    {
+        if (this._isPanelVisible(panelDescriptor.name()))
+            return;
+        this.element.appendChild(panelDescriptor._toolbarElement);
+        panelDescriptor._toolbarElement.removeStyleClass("hidden");
+        this._setPanelVisible(panelDescriptor.name(), true);
+        this._updatePanelsMenuState();
+        this.resize();
+    },
+
+    /**
+     * @param {WebInspector.PanelDescriptor} panelDescriptor
+     * @param {boolean=} noCloseButton
+     * @return {Element}
+     */
+    _createPanelToolbarItem: function(panelDescriptor, noCloseButton)
+    {
+        var toolbarItem = document.createElement("button");
+        toolbarItem.className = "toolbar-item toggleable";
+        toolbarItem.panelDescriptor = panelDescriptor;
+        toolbarItem.addStyleClass(panelDescriptor.name());
+
+        /**
+         * @param {Event} event
+         */
+        function onContextMenuEvent(event)
+        {
+            var contextMenu = new WebInspector.ContextMenu(event);
+            contextMenu.appendItem(WebInspector.UIString("Close"), this._hidePanel.bind(this, panelDescriptor));
+            contextMenu.show();
+        }
+        if (!this._isDefaultPanel(panelDescriptor.name()))
+            toolbarItem.addEventListener("contextmenu", onContextMenuEvent.bind(this), true);
+
+        function onToolbarItemClicked()
+        {
+            this._showPanel(panelDescriptor);
+            this._updateDropdownButtonAndHideDropdown();
+            WebInspector.inspectorView.setCurrentPanel(panelDescriptor.panel());
+        }
+        toolbarItem.addEventListener("click", onToolbarItemClicked.bind(this), false);
+
+        function onToolbarItemCloseButtonClicked(event)
+        {
+            event.stopPropagation();
+            this._hidePanel(panelDescriptor);
+        }
+
+        function panelSelected()
+        {
+            if (WebInspector.inspectorView.currentPanel() && panelDescriptor.name() === WebInspector.inspectorView.currentPanel().name)
+                toolbarItem.addStyleClass("toggled-on");
+            else
+                toolbarItem.removeStyleClass("toggled-on");
+        }
+        WebInspector.inspectorView.addEventListener(WebInspector.InspectorView.Events.PanelSelected, panelSelected);
+
+        var iconElement = toolbarItem.createChild("div", "toolbar-icon");
+        toolbarItem.createChild("div", "toolbar-label").textContent = panelDescriptor.title();
+        if (this._isToolbarCustomizable() && !this._isDefaultPanel(panelDescriptor.name()) && !noCloseButton) {
+            var closeButton = toolbarItem.createChild("div", "close-button");
+            closeButton.addEventListener("click", onToolbarItemCloseButtonClicked.bind(this), false);
+        }
+        if (panelDescriptor.iconURL()) {
+            iconElement.addStyleClass("custom-toolbar-icon");
+            iconElement.style.backgroundImage = "url(" + panelDescriptor.iconURL() + ")";
+        }
+        panelSelected();
+        return toolbarItem;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _isDockedToBottom: function()
+    {
+        return !!WebInspector.dockController && WebInspector.dockController.dockSide() == WebInspector.DockController.State.DockedToBottom;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _isUndocked: function()
+    {
+        return !!WebInspector.dockController && WebInspector.dockController.dockSide() == WebInspector.DockController.State.Undocked;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    _toolbarDragStart: function(event)
+    {
+        if (this._isUndocked())
+            return false;
+
+        var target = event.target;
+        if (target.hasStyleClass("toolbar-item") && target.hasStyleClass("toggleable"))
+            return false;
+
+        if (target !== this.element && !target.hasStyleClass("toolbar-item"))
+            return false;
+
+        this._lastScreenX = event.screenX;
+        this._lastScreenY = event.screenY;
+        this._lastHeightDuringDrag = window.innerHeight;
+        this._startDistanceToRight = window.innerWidth - event.clientX;
+        this._startDinstanceToBottom = window.innerHeight - event.clientY;
+        return true;
+    },
+
+    _toolbarDragEnd: function(event)
+    {
+        // We may not get the drag event at the end.
+        // Apply last changes manually.
+        this._toolbarDrag(event);
+        delete this._lastScreenX;
+        delete this._lastScreenY;
+        delete this._lastHeightDuringDrag;
+        delete this._startDistanceToRight;
+        delete this._startDinstanceToBottom;
+    },
+
+    _toolbarDrag: function(event)
+    {
+        event.preventDefault();
+
+        if (this._isUndocked())
+            return this._toolbarDragMoveWindow(event);
+
+        return this._toolbarDragChangeDocking(event);
+    },
+
+    _toolbarDragMoveWindow: function(event)
+    {
+        var x = event.screenX - this._lastScreenX;
+        var y = event.screenY - this._lastScreenY;
+        this._lastScreenX = event.screenX;
+        this._lastScreenY = event.screenY;
+        InspectorFrontendHost.moveWindowBy(x, y);
+    },
+
+    _toolbarDragChangeDocking: function(event)
+    {
+        if (this._isDockedToBottom()) {
+            var distanceToRight = window.innerWidth - event.clientX;
+            if (distanceToRight < this._startDistanceToRight * 2 / 3) {
+                InspectorFrontendHost.requestSetDockSide(WebInspector.DockController.State.DockedToRight);
+                return true;
+            }
+        } else {
+            var distanceToBottom = window.innerHeight - event.clientY;
+            if (distanceToBottom < this._startDinstanceToBottom * 2 / 3) {
+                InspectorFrontendHost.requestSetDockSide(WebInspector.DockController.State.DockedToBottom);
+                return true;
+            }
+        }
+    },
+
+    _onClose: function()
+    {
+        WebInspector.close();
+    },
+
+    _setDropdownVisible: function(visible)
+    {
+        if (!this._dropdown) {
+            if (!visible)
+                return;
+            this._dropdown = new WebInspector.ToolbarDropdown(this);
+        }
+        if (visible)
+            this._dropdown.show();
+        else
+            this._dropdown.hide();
+    },
+
+    _toggleDropdown: function()
+    {
+        this._setDropdownVisible(!this._dropdown || !this._dropdown.visible);
+    },
+
+    _togglePanelsMenu: function(event)
+    {
+        function activatePanel(panelDescriptor)
+        {
+            this._showPanel(panelDescriptor);
+            WebInspector.showPanel(panelDescriptor.name());
+        }
+
+        var contextMenu = new WebInspector.ContextMenu(event);
+        var currentPanelName = WebInspector.inspectorView.currentPanel().name;
+        var toolbarItems = this.element.querySelectorAll(".toolbar-item.toggleable");
+        for (var i = 0; i < toolbarItems.length; ++i) {
+            if (toolbarItems[i].offsetTop >= toolbarItems[0].offsetHeight) {
+                var descr = toolbarItems[i].panelDescriptor;
+                if (descr.name() === currentPanelName)
+                    contextMenu.appendCheckboxItem(descr.title(), activatePanel.bind(this, descr), true);
+                else
+                    contextMenu.appendItem(descr.title(), activatePanel.bind(this, descr));
+            }
+        }
+        contextMenu.appendSeparator();
+        for (var i = 0; i < this._panelDescriptors.length; ++i) {
+            var descr = this._panelDescriptors[i];
+            if (this._isPanelVisible(descr.name()))
+                continue;
+            contextMenu.appendItem(descr.title(), activatePanel.bind(this, descr));
+        }
+
+        contextMenu.showSoftMenu();
+    },
+
+    _updateDropdownButtonAndHideDropdown: function()
+    {
+        WebInspector.invokeOnceAfterBatchUpdate(this, this._innerUpdateDropdownButtonAndHideDropdown);
+    },
+
+    _innerUpdateDropdownButtonAndHideDropdown: function()
+    {
+        if (this._isToolbarCustomizable()) {
+            this._updatePanelsMenuState();
+            return;
+        }
+        this._setDropdownVisible(false);
+
+        if (this.element.scrollHeight > this.element.offsetHeight)
+            this._dropdownButton.removeStyleClass("hidden");
+        else
+            this._dropdownButton.addStyleClass("hidden");
+    }
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.Toolbar} toolbar
+ */
+WebInspector.ToolbarDropdown = function(toolbar)
+{
+    this._toolbar = toolbar;
+    this._arrow = document.getElementById("toolbar-dropdown-arrow");
+    this.element = document.createElement("div");
+    this.element.id = "toolbar-dropdown";
+    this.element.className = "toolbar-small";
+    this._contentElement = this.element.createChild("div", "scrollable-content");
+    this._contentElement.tabIndex = 0;
+    this._contentElement.addEventListener("keydown", this._onKeyDown.bind(this), true);
+}
+
+WebInspector.ToolbarDropdown.prototype = {
+    show: function()
+    {
+        if (this.visible)
+            return;
+        var style = this.element.style;
+        this._populate();
+        var top = this._arrow.totalOffsetTop() + this._arrow.clientHeight;
+        this._arrow.addStyleClass("dropdown-visible");
+        this.element.style.top = top + "px";
+        this.element.style.right = window.innerWidth - this._arrow.totalOffsetLeft() - this._arrow.clientWidth + "px";
+        this._contentElement.style.maxHeight = window.innerHeight - top - 20 + "px";
+        this._toolbar.element.appendChild(this.element);
+    },
+
+    hide: function()
+    {
+        if (!this.visible)
+            return;
+        this._arrow.removeStyleClass("dropdown-visible");
+        this.element.parentNode.removeChild(this.element);
+        this._contentElement.removeChildren();
+    },
+
+    get visible()
+    {
+        return !!this.element.parentNode;
+    },
+
+    _populate: function()
+    {
+        var toolbarItems = this._toolbar.element.querySelectorAll(".toolbar-item.toggleable");
+
+        var needsSeparator = false;
+        for (var i = 0; i < toolbarItems.length; ++i) {
+            if (toolbarItems[i].offsetTop >= toolbarItems[0].offsetHeight) {
+                this._contentElement.appendChild(this._toolbar._createPanelToolbarItem(toolbarItems[i].panelDescriptor, true));
+                needsSeparator = true;
+            }
+        }
+
+        var panelDescriptors = this._toolbar._panelDescriptors;
+        for (var i = 0; i < panelDescriptors.length; ++i) {
+            var descr = panelDescriptors[i];
+            if (this._toolbar._isPanelVisible(descr.name()))
+                continue;
+            if (needsSeparator) {
+                this._contentElement.createChild("div", "toolbar-items-separator");
+                needsSeparator = false;
+            }
+            this._contentElement.appendChild(this._toolbar._createPanelToolbarItem(descr, true));
+        }
+    },
+
+    _onKeyDown: function(event)
+    {
+        if (event.keyCode !== WebInspector.KeyboardShortcut.Keys.Esc.code)
+            return;
+        event.consume();
+        this.hide();
+    }
+}
+
+/**
+ * @type {?WebInspector.Toolbar}
+ */
+WebInspector.toolbar = null;
diff --git a/Source/devtools/front_end/TopDownProfileDataGridTree.js b/Source/devtools/front_end/TopDownProfileDataGridTree.js
new file mode 100644
index 0000000..8034fe9
--- /dev/null
+++ b/Source/devtools/front_end/TopDownProfileDataGridTree.js
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2009 280 North 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileDataGridNode}
+ * @param {!ProfilerAgent.CPUProfileNode} profileNode
+ * @param {!WebInspector.TopDownProfileDataGridTree} owningTree
+ */
+WebInspector.TopDownProfileDataGridNode = function(profileNode, owningTree)
+{
+    var hasChildren = !!(profileNode.children && profileNode.children.length);
+
+    WebInspector.ProfileDataGridNode.call(this, profileNode, owningTree, hasChildren);
+
+    this._remainingChildren = profileNode.children;
+}
+
+WebInspector.TopDownProfileDataGridNode.prototype = {
+    _sharedPopulate: function()
+    {
+        var children = this._remainingChildren;
+        var childrenLength = children.length;
+
+        for (var i = 0; i < childrenLength; ++i)
+            this.appendChild(new WebInspector.TopDownProfileDataGridNode(children[i], this.tree));
+
+        this._remainingChildren = null;
+    },
+
+    _exclude: function(aCallUID)
+    {
+        if (this._remainingChildren)
+            this.populate();
+
+        this._save();
+
+        var children = this.children;
+        var index = this.children.length;
+
+        while (index--)
+            children[index]._exclude(aCallUID);
+
+        var child = this.childrenByCallUID[aCallUID];
+
+        if (child)
+            this._merge(child, true);
+    },
+
+    __proto__: WebInspector.ProfileDataGridNode.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ProfileDataGridTree}
+ * @param {WebInspector.CPUProfileView} profileView
+ * @param {ProfilerAgent.CPUProfileNode} rootProfileNode
+ */
+WebInspector.TopDownProfileDataGridTree = function(profileView, rootProfileNode)
+{
+    WebInspector.ProfileDataGridTree.call(this, profileView, rootProfileNode);
+
+    this._remainingChildren = rootProfileNode.children;
+
+    var any = /** @type{*} */(this);
+    var node = /** @type{WebInspector.ProfileDataGridNode} */(any);
+    WebInspector.TopDownProfileDataGridNode.prototype.populate.call(node);
+}
+
+WebInspector.TopDownProfileDataGridTree.prototype = {
+    /**
+     * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+     */
+    focus: function(profileDataGridNode)
+    {
+        if (!profileDataGridNode)
+            return;
+
+        this._save();
+        profileDataGridNode.savePosition();
+
+        this.children = [profileDataGridNode];
+        this.totalTime = profileDataGridNode.totalTime;
+    },
+
+    /**
+     * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
+     */
+    exclude: function(profileDataGridNode)
+    {
+        if (!profileDataGridNode)
+            return;
+
+        this._save();
+
+        var excludedCallUID = profileDataGridNode.callUID;
+
+        var any = /** @type{*} */(this);
+        var node = /** @type{WebInspector.TopDownProfileDataGridNode} */(any);
+        WebInspector.TopDownProfileDataGridNode.prototype._exclude.call(node, excludedCallUID);
+
+        if (this.lastComparator)
+            this.sort(this.lastComparator, true);
+    },
+
+    restore: function()
+    {
+        if (!this._savedChildren)
+            return;
+
+        this.children[0].restorePosition();
+
+        WebInspector.ProfileDataGridTree.prototype.restore.call(this);
+    },
+
+    _merge: WebInspector.TopDownProfileDataGridNode.prototype._merge,
+
+    _sharedPopulate: WebInspector.TopDownProfileDataGridNode.prototype._sharedPopulate,
+
+    __proto__: WebInspector.ProfileDataGridTree.prototype
+}
diff --git a/Source/devtools/front_end/UISourceCode.js b/Source/devtools/front_end/UISourceCode.js
new file mode 100644
index 0000000..79c0b53
--- /dev/null
+++ b/Source/devtools/front_end/UISourceCode.js
@@ -0,0 +1,1003 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ * @implements {WebInspector.ContentProvider}
+ * @param {WebInspector.Project} project
+ * @param {Array.<string>} path
+ * @param {string} url
+ * @param {WebInspector.ResourceType} contentType
+ * @param {boolean} isEditable
+ */
+WebInspector.UISourceCode = function(project, path, originURL, url, contentType, isEditable)
+{
+    this._project = project;
+    this._path = path;
+    this._originURL = originURL;
+    this._url = url;
+    this._contentType = contentType;
+    this._isEditable = isEditable;
+    /**
+     * @type Array.<function(?string,boolean,string)>
+     */
+    this._requestContentCallbacks = [];
+    this._liveLocations = new Set();
+    /**
+     * @type {Array.<WebInspector.PresentationConsoleMessage>}
+     */
+    this._consoleMessages = [];
+    
+    /**
+     * @type {Array.<WebInspector.Revision>}
+     */
+    this.history = [];
+    if (this.isEditable() && this._url)
+        this._restoreRevisionHistory();
+    this._formatterMapping = new WebInspector.IdentityFormatterSourceMapping();
+}
+
+WebInspector.UISourceCode.Events = {
+    FormattedChanged: "FormattedChanged",
+    WorkingCopyChanged: "WorkingCopyChanged",
+    WorkingCopyCommitted: "WorkingCopyCommitted",
+    TitleChanged: "TitleChanged",
+    ConsoleMessageAdded: "ConsoleMessageAdded",
+    ConsoleMessageRemoved: "ConsoleMessageRemoved",
+    ConsoleMessagesCleared: "ConsoleMessagesCleared",
+    SourceMappingChanged: "SourceMappingChanged",
+}
+
+WebInspector.UISourceCode.prototype = {
+    /**
+     * @return {string}
+     */
+    get url()
+    {
+        return this._url;
+    },
+
+    /**
+     * @return {Array.<string>}
+     */
+    path: function()
+    {
+        return this._path;
+    },
+
+    /**
+     * @return {string}
+     */
+    name: function()
+    {
+        return this._path[this._path.length - 1];
+    },
+
+    /**
+     * @return {string}
+     */
+    displayName: function()
+    {
+        var displayName = this.name() || (this._project.displayName() + "/" + this._path.join("/"));
+        return displayName.trimEnd(100);
+    },
+
+    /**
+     * @return {string}
+     */
+    uri: function()
+    {
+        if (!this._project.id())
+            return this._path.join("/");
+        if (!this._path.length)
+            return this._project.id();
+        return this._project.id() + "/" + this._path.join("/");
+    },
+
+    /**
+     * @return {string}
+     */
+    originURL: function()
+    {
+        return this._originURL;
+    },
+
+    /**
+     * @param {string} newName
+     */
+    rename: function(newName)
+    {
+        if (!this._path.length)
+            return;
+        this._path[this._path.length - 1] = newName;
+        this._url = newName;
+        this._originURL = newName;
+        this.dispatchEventToListeners(WebInspector.UISourceCode.Events.TitleChanged, null);
+    },
+
+    /**
+     * @return {string}
+     */
+    contentURL: function()
+    {
+        return this.originURL();
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function()
+    {
+        return this._contentType;
+    },
+
+    /**
+     * @return {WebInspector.ScriptFile}
+     */
+    scriptFile: function()
+    {
+        return this._scriptFile;
+    },
+
+    /**
+     * @param {WebInspector.ScriptFile} scriptFile
+     */
+    setScriptFile: function(scriptFile)
+    {
+        this._scriptFile = scriptFile;
+    },
+
+    /**
+     * @return {WebInspector.StyleFile}
+     */
+    styleFile: function()
+    {
+        return this._styleFile;
+    },
+
+    /**
+     * @param {WebInspector.StyleFile} styleFile
+     */
+    setStyleFile: function(styleFile)
+    {
+        this._styleFile = styleFile;
+    },
+
+    /**
+     * @return {WebInspector.Project}
+     */
+    project: function()
+    {
+        return this._project;
+    },
+
+    /**
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestContent: function(callback)
+    {
+        if (this._content || this._contentLoaded) {
+            callback(this._content, false, this._mimeType);
+            return;
+        }
+        this._requestContentCallbacks.push(callback);
+        if (this._requestContentCallbacks.length === 1)
+            this._project.requestFileContent(this, this._fireContentAvailable.bind(this));
+    },
+
+    checkContentUpdated: function()
+    {
+        if (!this._project.canSetFileContent())
+            return;
+        if (this._checkingContent)
+            return;
+        this._checkingContent = true;
+        this._project.requestFileContent(this, contentLoaded.bind(this));
+
+        function contentLoaded(updatedContent)
+        {
+            if (updatedContent === null) {
+                var workingCopy = this.workingCopy();
+                this._commitContent("", false);
+                this.setWorkingCopy(workingCopy);
+                delete this._checkingContent;
+                return;
+            }
+            if (typeof this._lastAcceptedContent === "string" && this._lastAcceptedContent === updatedContent) {
+                delete this._checkingContent;
+                return;
+            }
+            if (this._content === updatedContent) {
+                delete this._lastAcceptedContent;
+                delete this._checkingContent;
+                return;
+            }
+
+            if (!this.isDirty()) {
+                this._commitContent(updatedContent, false);
+                delete this._checkingContent;
+                return;
+            }
+
+            var shouldUpdate = window.confirm(WebInspector.UIString("This file was changed externally. Would you like to reload it?"));
+            if (shouldUpdate)
+                this._commitContent(updatedContent, false);
+            else
+                this._lastAcceptedContent = updatedContent;
+            delete this._checkingContent;
+        }
+    },
+
+    /**
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestOriginalContent: function(callback)
+    {
+        this._project.requestFileContent(this, callback);
+    },
+
+    /**
+     * @param {string} content
+     * @param {boolean} shouldSetContentInProject
+     */
+    _commitContent: function(content, shouldSetContentInProject)
+    {
+        delete this._lastAcceptedContent;
+        this._content = content;
+        this._contentLoaded = true;
+
+        var lastRevision = this.history.length ? this.history[this.history.length - 1] : null;
+        if (!lastRevision || lastRevision._content !== this._content) {
+            var revision = new WebInspector.Revision(this, this._content, new Date());
+            this.history.push(revision);
+            revision._persist();
+        }
+
+        this._innerResetWorkingCopy();
+        this.dispatchEventToListeners(WebInspector.UISourceCode.Events.WorkingCopyCommitted);
+        if (this._url && WebInspector.fileManager.isURLSaved(this._url)) {
+            WebInspector.fileManager.save(this._url, this._content, false);
+            WebInspector.fileManager.close(this._url);
+        }
+        if (shouldSetContentInProject)
+            this._project.setFileContent(this, this._content, function() { });
+    },
+
+    /**
+     * @param {string} content
+     */
+    addRevision: function(content)
+    {
+        this._commitContent(content, true);
+    },
+
+    _restoreRevisionHistory: function()
+    {
+        if (!window.localStorage)
+            return;
+
+        var registry = WebInspector.Revision._revisionHistoryRegistry();
+        var historyItems = registry[this.url];
+        if (!historyItems)
+            return;
+
+        function filterOutStale(historyItem)
+        {
+            return historyItem.loaderId === WebInspector.resourceTreeModel.mainFrame.loaderId;
+        }
+
+        historyItems = historyItems.filter(filterOutStale);
+        if (!historyItems.length)
+            return;
+
+        for (var i = 0; i < historyItems.length; ++i) {
+            var content = window.localStorage[historyItems[i].key];
+            var timestamp = new Date(historyItems[i].timestamp);
+            var revision = new WebInspector.Revision(this, content, timestamp);
+            this.history.push(revision);
+        }
+        this._content = this.history[this.history.length - 1].content;
+        this._contentLoaded = true;
+        this._mimeType = this.canonicalMimeType();
+    },
+
+    _clearRevisionHistory: function()
+    {
+        if (!window.localStorage)
+            return;
+
+        var registry = WebInspector.Revision._revisionHistoryRegistry();
+        var historyItems = registry[this.url];
+        for (var i = 0; historyItems && i < historyItems.length; ++i)
+            delete window.localStorage[historyItems[i].key];
+        delete registry[this.url];
+        window.localStorage["revision-history"] = JSON.stringify(registry);
+    },
+   
+    revertToOriginal: function()
+    {
+        /**
+         * @this {WebInspector.UISourceCode}
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function callback(content, contentEncoded, mimeType)
+        {
+            if (typeof content !== "string")
+                return;
+
+            this.addRevision(content);
+        }
+
+        this.requestOriginalContent(callback.bind(this));
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.ApplyOriginalContent,
+            url: this.url
+        });
+    },
+
+    /**
+     * @param {function(WebInspector.UISourceCode)} callback
+     */
+    revertAndClearHistory: function(callback)
+    {
+        /**
+         * @this {WebInspector.UISourceCode}
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function revert(content, contentEncoded, mimeType)
+        {
+            if (typeof content !== "string")
+                return;
+
+            this.addRevision(content);
+            this._clearRevisionHistory();
+            this.history = [];
+            callback(this);
+        }
+
+        this.requestOriginalContent(revert.bind(this));
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.RevertRevision,
+            url: this.url
+        });
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isEditable: function()
+    {
+        return this._isEditable;
+    },
+
+    /**
+     * @return {string}
+     */
+    workingCopy: function()
+    {
+        if (this._workingCopyGetter) {
+            this._workingCopy = this._workingCopyGetter();
+            delete this._workingCopyGetter;
+        }
+        if (this.isDirty())
+            return this._workingCopy;
+        return this._content;
+    },
+
+    resetWorkingCopy: function()
+    {
+        this._innerResetWorkingCopy();
+        this.dispatchEventToListeners(WebInspector.UISourceCode.Events.WorkingCopyChanged);
+    },
+
+    _innerResetWorkingCopy: function()
+    {
+        delete this._workingCopy;
+        delete this._workingCopyGetter;
+    },
+
+    /**
+     * @param {string} newWorkingCopy
+     */
+    setWorkingCopy: function(newWorkingCopy)
+    {
+        this._mimeType = this.canonicalMimeType();
+        this._workingCopy = newWorkingCopy;
+        delete this._workingCopyGetter;
+        this.dispatchEventToListeners(WebInspector.UISourceCode.Events.WorkingCopyChanged);
+    },
+
+    setWorkingCopyGetter: function(workingCopyGetter)
+    {
+        this._workingCopyGetter = workingCopyGetter;
+        this.dispatchEventToListeners(WebInspector.UISourceCode.Events.WorkingCopyChanged);
+    },
+
+    removeWorkingCopyGetter: function()
+    {
+        if (!this._workingCopyGetter)
+            return;
+        this._workingCopy = this._workingCopyGetter();
+        delete this._workingCopyGetter;
+    },
+
+    /**
+     * @param {function(?string)} callback
+     */
+    commitWorkingCopy: function(callback)
+    {
+        if (!this.isDirty()) {
+            callback(null);
+            return;
+        }
+
+        this._commitContent(this.workingCopy(), true);
+        callback(null);
+
+        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
+            action: WebInspector.UserMetrics.UserActionNames.FileSaved,
+            url: this.url
+        });
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isDirty: function()
+    {
+        return typeof this._workingCopy !== "undefined" || typeof this._workingCopyGetter !== "undefined";
+    },
+
+    /**
+     * @return {string}
+     */
+    mimeType: function()
+    {
+        return this._mimeType;
+    },
+
+    /**
+     * @return {string}
+     */
+    canonicalMimeType: function()
+    {
+        return this.contentType().canonicalMimeType() || this._mimeType;
+    },
+
+    /**
+     * @return {?string}
+     */
+    content: function()
+    {
+        return this._content;
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback)
+    {
+        var content = this.content();
+        if (content) {
+            var provider = new WebInspector.StaticContentProvider(this.contentType(), content);
+            provider.searchInContent(query, caseSensitive, isRegex, callback);
+            return;
+        }
+
+        this._project.searchInFileContent(this, query, caseSensitive, isRegex, callback);
+    },
+
+    /**
+     * @param {?string} content
+     * @param {boolean} contentEncoded
+     * @param {string} mimeType
+     */
+    _fireContentAvailable: function(content, contentEncoded, mimeType)
+    {
+        this._contentLoaded = true;
+        this._mimeType = mimeType;
+        this._content = content;
+
+        var callbacks = this._requestContentCallbacks.slice();
+        this._requestContentCallbacks = [];
+        for (var i = 0; i < callbacks.length; ++i)
+            callbacks[i](content, contentEncoded, mimeType);
+
+        if (this._formatOnLoad) {
+            delete this._formatOnLoad;
+            this.setFormatted(true);
+        }
+    },
+
+    /**
+     * @return {boolean}
+     */
+    contentLoaded: function()
+    {
+        return this._contentLoaded;
+    },
+
+    /**
+     * @param {number} lineNumber
+     * @param {number} columnNumber
+     * @return {WebInspector.RawLocation}
+     */
+    uiLocationToRawLocation: function(lineNumber, columnNumber)
+    {
+        if (!this._sourceMapping)
+            return null;
+        var location = this._formatterMapping.formattedToOriginal(lineNumber, columnNumber);
+        return this._sourceMapping.uiLocationToRawLocation(this, location[0], location[1]);
+    },
+
+    /**
+     * @param {!WebInspector.LiveLocation} liveLocation
+     */
+    addLiveLocation: function(liveLocation)
+    {
+        this._liveLocations.add(liveLocation);
+    },
+
+    /**
+     * @param {!WebInspector.LiveLocation} liveLocation
+     */
+    removeLiveLocation: function(liveLocation)
+    {
+        this._liveLocations.remove(liveLocation);
+    },
+
+    updateLiveLocations: function()
+    {
+        var items = this._liveLocations.items();
+        for (var i = 0; i < items.length; ++i)
+            items[i].update();
+    },
+
+    /**
+     * @param {WebInspector.UILocation} uiLocation
+     */
+    overrideLocation: function(uiLocation)
+    {
+        var location = this._formatterMapping.originalToFormatted(uiLocation.lineNumber, uiLocation.columnNumber);
+        uiLocation.lineNumber = location[0];
+        uiLocation.columnNumber = location[1];
+        return uiLocation;
+    },
+
+    /**
+     * @return {Array.<WebInspector.PresentationConsoleMessage>}
+     */
+    consoleMessages: function()
+    {
+        return this._consoleMessages;
+    },
+
+    /**
+     * @param {WebInspector.PresentationConsoleMessage} message
+     */
+    consoleMessageAdded: function(message)
+    {
+        this._consoleMessages.push(message);
+        this.dispatchEventToListeners(WebInspector.UISourceCode.Events.ConsoleMessageAdded, message);
+    },
+
+    /**
+     * @param {WebInspector.PresentationConsoleMessage} message
+     */
+    consoleMessageRemoved: function(message)
+    {
+        this._consoleMessages.remove(message);
+        this.dispatchEventToListeners(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, message);
+    },
+
+    consoleMessagesCleared: function()
+    {
+        this._consoleMessages = [];
+        this.dispatchEventToListeners(WebInspector.UISourceCode.Events.ConsoleMessagesCleared);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    formatted: function()
+    {
+        return !!this._formatted;
+    },
+
+    /**
+     * @param {boolean} formatted
+     */
+    setFormatted: function(formatted)
+    {
+        if (!this.contentLoaded()) {
+            this._formatOnLoad = formatted;
+            return;
+        }
+
+        if (this._formatted === formatted)
+            return;
+
+        this._formatted = formatted;
+
+        // Re-request content
+        this._contentLoaded = false;
+        this._content = false;
+        WebInspector.UISourceCode.prototype.requestContent.call(this, didGetContent.bind(this));
+  
+        /**
+         * @this {WebInspector.UISourceCode}
+         * @param {?string} content
+         * @param {boolean} contentEncoded
+         * @param {string} mimeType
+         */
+        function didGetContent(content, contentEncoded, mimeType)
+        {
+            var formatter;
+            if (!formatted)
+                formatter = new WebInspector.IdentityFormatter();
+            else
+                formatter = WebInspector.Formatter.createFormatter(this.contentType());
+            formatter.formatContent(mimeType, content || "", formattedChanged.bind(this));
+  
+            /**
+             * @this {WebInspector.UISourceCode}
+             * @param {string} content
+             * @param {WebInspector.FormatterSourceMapping} formatterMapping
+             */
+            function formattedChanged(content, formatterMapping)
+            {
+                this._content = content;
+                this._innerResetWorkingCopy();
+                this._formatterMapping = formatterMapping;
+                this.dispatchEventToListeners(WebInspector.UISourceCode.Events.FormattedChanged, {content: content});
+                this.updateLiveLocations();
+            }
+        }
+    },
+
+    /**
+     * @return {WebInspector.Formatter} formatter
+     */
+    createFormatter: function()
+    {
+        // overridden by subclasses.
+        return null;
+    },
+
+    /**
+     * @param {WebInspector.SourceMapping} sourceMapping
+     */
+    setSourceMapping: function(sourceMapping)
+    {
+        var wasIdentity = this._sourceMapping ? this._sourceMapping.isIdentity() : true;
+        this._sourceMapping = sourceMapping;
+        var data = {}
+        data.isIdentity = sourceMapping ? sourceMapping.isIdentity() : true;
+        data.identityHasChanged = data.isIdentity !== wasIdentity;
+        this.dispatchEventToListeners(WebInspector.UISourceCode.Events.SourceMappingChanged, data);
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @interface
+ * @extends {WebInspector.EventTarget}
+ */
+WebInspector.UISourceCodeProvider = function()
+{
+}
+
+WebInspector.UISourceCodeProvider.Events = {
+    UISourceCodeAdded: "UISourceCodeAdded",
+    UISourceCodeRemoved: "UISourceCodeRemoved"
+}
+
+WebInspector.UISourceCodeProvider.prototype = {
+    /**
+     * @return {Array.<WebInspector.UISourceCode>}
+     */
+    uiSourceCodes: function() {},
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ * @param {number} lineNumber
+ * @param {number} columnNumber
+ */
+WebInspector.UILocation = function(uiSourceCode, lineNumber, columnNumber)
+{
+    this.uiSourceCode = uiSourceCode;
+    this.lineNumber = lineNumber;
+    this.columnNumber = columnNumber;
+}
+
+WebInspector.UILocation.prototype = {
+    /**
+     * @return {WebInspector.RawLocation}
+     */
+    uiLocationToRawLocation: function()
+    {
+        return this.uiSourceCode.uiLocationToRawLocation(this.lineNumber, this.columnNumber);
+    },
+
+    /**
+     * @return {?string}
+     */
+    url: function()
+    {
+        return this.uiSourceCode.contentURL();
+    },
+
+    /**
+     * @return {string}
+     */
+    linkText: function()
+    {
+        var linkText = this.uiSourceCode.name() || (this.uiSourceCode.project().displayName() + "/" + this.uiSourceCode.path().join("/"));
+        if (typeof this.lineNumber === "number")
+            linkText += ":" + (this.lineNumber + 1);
+        return linkText;
+    }
+}
+
+/**
+ * @interface
+ */
+WebInspector.RawLocation = function()
+{
+}
+
+/**
+ * @constructor
+ * @param {WebInspector.RawLocation} rawLocation
+ * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
+ */
+WebInspector.LiveLocation = function(rawLocation, updateDelegate)
+{
+    this._rawLocation = rawLocation;
+    this._updateDelegate = updateDelegate;
+    this._uiSourceCodes = [];
+}
+
+WebInspector.LiveLocation.prototype = {
+    update: function()
+    {
+        var uiLocation = this.uiLocation();
+        if (uiLocation) {
+            var uiSourceCode = uiLocation.uiSourceCode;
+            if (this._uiSourceCodes.indexOf(uiSourceCode) === -1) {
+                uiSourceCode.addLiveLocation(this);
+                this._uiSourceCodes.push(uiSourceCode);
+            }
+            var oneTime = this._updateDelegate(uiLocation);
+            if (oneTime)
+                this.dispose();
+        }
+    },
+
+    /**
+     * @return {WebInspector.RawLocation}
+     */
+    rawLocation: function()
+    {
+        return this._rawLocation;
+    },
+
+    /**
+     * @return {WebInspector.UILocation}
+     */
+    uiLocation: function()
+    {
+        // Should be overridden by subclasses.
+    },
+
+    dispose: function()
+    {
+        for (var i = 0; i < this._uiSourceCodes.length; ++i)
+            this._uiSourceCodes[i].removeLiveLocation(this);
+        this._uiSourceCodes = [];
+    }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.ContentProvider}
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ * @param {?string|undefined} content
+ * @param {Date} timestamp
+ */
+WebInspector.Revision = function(uiSourceCode, content, timestamp)
+{
+    this._uiSourceCode = uiSourceCode;
+    this._content = content;
+    this._timestamp = timestamp;
+}
+
+WebInspector.Revision._revisionHistoryRegistry = function()
+{
+    if (!WebInspector.Revision._revisionHistoryRegistryObject) {
+        if (window.localStorage) {
+            var revisionHistory = window.localStorage["revision-history"];
+            try {
+                WebInspector.Revision._revisionHistoryRegistryObject = revisionHistory ? JSON.parse(revisionHistory) : {};
+            } catch (e) {
+                WebInspector.Revision._revisionHistoryRegistryObject = {};
+            }
+        } else
+            WebInspector.Revision._revisionHistoryRegistryObject = {};
+    }
+    return WebInspector.Revision._revisionHistoryRegistryObject;
+}
+
+WebInspector.Revision.filterOutStaleRevisions = function()
+{
+    if (!window.localStorage)
+        return;
+
+    var registry = WebInspector.Revision._revisionHistoryRegistry();
+    var filteredRegistry = {};
+    for (var url in registry) {
+        var historyItems = registry[url];
+        var filteredHistoryItems = [];
+        for (var i = 0; historyItems && i < historyItems.length; ++i) {
+            var historyItem = historyItems[i];
+            if (historyItem.loaderId === WebInspector.resourceTreeModel.mainFrame.loaderId) {
+                filteredHistoryItems.push(historyItem);
+                filteredRegistry[url] = filteredHistoryItems;
+            } else
+                delete window.localStorage[historyItem.key];
+        }
+    }
+    WebInspector.Revision._revisionHistoryRegistryObject = filteredRegistry;
+
+    function persist()
+    {
+        window.localStorage["revision-history"] = JSON.stringify(filteredRegistry);
+    }
+
+    // Schedule async storage.
+    setTimeout(persist, 0);
+}
+
+WebInspector.Revision.prototype = {
+    /**
+     * @return {WebInspector.UISourceCode}
+     */
+    get uiSourceCode()
+    {
+        return this._uiSourceCode;
+    },
+
+    /**
+     * @return {Date}
+     */
+    get timestamp()
+    {
+        return this._timestamp;
+    },
+
+    /**
+     * @return {?string}
+     */
+    get content()
+    {
+        return this._content || null;
+    },
+
+    revertToThis: function()
+    {
+        function revert(content)
+        {
+            if (this._uiSourceCode._content !== content)
+                this._uiSourceCode.addRevision(content);
+        }
+        this.requestContent(revert.bind(this));
+    },
+
+    /**
+     * @return {string}
+     */
+    contentURL: function()
+    {
+        return this._uiSourceCode.originURL();
+    },
+
+    /**
+     * @return {WebInspector.ResourceType}
+     */
+    contentType: function()
+    {
+        return this._uiSourceCode.contentType();
+    },
+
+    /**
+     * @param {function(?string, boolean, string)} callback
+     */
+    requestContent: function(callback)
+    {
+        callback(this._content || "", false, this.uiSourceCode.canonicalMimeType());
+    },
+
+    /**
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInContent: function(query, caseSensitive, isRegex, callback)
+    {
+        callback([]);
+    },
+
+    _persist: function()
+    {
+        if (!window.localStorage)
+            return;
+
+        var url = this.contentURL();
+        if (!url || url.startsWith("inspector://"))
+            return;
+
+        var loaderId = WebInspector.resourceTreeModel.mainFrame.loaderId;
+        var timestamp = this.timestamp.getTime();
+        var key = "revision-history|" + url + "|" + loaderId + "|" + timestamp;
+
+        var registry = WebInspector.Revision._revisionHistoryRegistry();
+
+        var historyItems = registry[url];
+        if (!historyItems) {
+            historyItems = [];
+            registry[url] = historyItems;
+        }
+        historyItems.push({url: url, loaderId: loaderId, timestamp: timestamp, key: key});
+
+        function persist()
+        {
+            window.localStorage[key] = this._content;
+            window.localStorage["revision-history"] = JSON.stringify(registry);
+        }
+
+        // Schedule async storage.
+        setTimeout(persist.bind(this), 0);
+    }
+}
diff --git a/Source/devtools/front_end/UISourceCodeFrame.js b/Source/devtools/front_end/UISourceCodeFrame.js
new file mode 100644
index 0000000..197aef1
--- /dev/null
+++ b/Source/devtools/front_end/UISourceCodeFrame.js
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2012 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 GOOGLE 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 GOOGLE 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SourceFrame}
+ * @param {WebInspector.UISourceCode} uiSourceCode
+ */
+WebInspector.UISourceCodeFrame = function(uiSourceCode)
+{
+    this._uiSourceCode = uiSourceCode;
+    WebInspector.SourceFrame.call(this, this._uiSourceCode);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._onFormattedChanged, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._onWorkingCopyChanged, this);
+    this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCopyCommitted, this);
+}
+
+WebInspector.UISourceCodeFrame.prototype = {
+    wasShown: function()
+    {
+        WebInspector.SourceFrame.prototype.wasShown.call(this);
+        this._boundWindowFocused = this._windowFocused.bind(this);
+        window.addEventListener("focus", this._boundWindowFocused, false);
+        this._checkContentUpdated();
+    },
+
+    willHide: function()
+    {
+        WebInspector.SourceFrame.prototype.willHide.call(this);
+        window.removeEventListener("focus", this._boundWindowFocused, false);
+        delete this._boundWindowFocused;
+        this._uiSourceCode.removeWorkingCopyGetter();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canEditSource: function()
+    {
+        return this._uiSourceCode.isEditable();
+    },
+
+    _windowFocused: function(event)
+    {
+        this._checkContentUpdated();
+    },
+
+    _checkContentUpdated: function()
+    {
+        if (!this.loaded || !this.isShowing())
+            return;
+        this._uiSourceCode.checkContentUpdated();
+    },
+
+    /**
+     * @param {string} text
+     */
+    commitEditing: function(text)
+    {
+        if (!this._uiSourceCode.isDirty())
+            return;
+
+        this._muteSourceCodeEvents = true;
+        this._uiSourceCode.commitWorkingCopy(this._didEditContent.bind(this));
+        delete this._muteSourceCodeEvents;
+    },
+
+    onTextChanged: function(oldRange, newRange)
+    {
+        WebInspector.SourceFrame.prototype.onTextChanged.call(this, oldRange, newRange);
+        if (this._isSettingContent)
+            return;
+        this._muteSourceCodeEvents = true;
+        if (this._textEditor.isClean())
+            this._uiSourceCode.resetWorkingCopy();
+        else
+            this._uiSourceCode.setWorkingCopyGetter(this._textEditor.text.bind(this._textEditor));
+        delete this._muteSourceCodeEvents;
+    },
+
+    _didEditContent: function(error)
+    {
+        if (error) {
+            WebInspector.log(error, WebInspector.ConsoleMessage.MessageLevel.Error, true);
+            return;
+        }
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onFormattedChanged: function(event)
+    {
+        var content = /** @type {string} */ (event.data.content);
+        this._textEditor.setReadOnly(this._uiSourceCode.formatted());
+        this._innerSetContent(content);
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onWorkingCopyChanged: function(event)
+    {
+        if (this._muteSourceCodeEvents)
+            return;
+        this._innerSetContent(this._uiSourceCode.workingCopy());
+        this.onUISourceCodeContentChanged();
+    },
+
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _onWorkingCopyCommitted: function(event)
+    {
+        this._textEditor.markClean();
+        if (this._muteSourceCodeEvents)
+            return;
+        this._innerSetContent(this._uiSourceCode.workingCopy());
+        this.onUISourceCodeContentChanged();
+    },
+
+    onUISourceCodeContentChanged: function()
+    {
+    },
+
+    /**
+     * @param {string} content
+     */
+    _innerSetContent: function(content)
+    {
+        this._isSettingContent = true;
+        this.setContent(content, false, this._uiSourceCode.mimeType());
+        delete this._isSettingContent;
+    },
+
+    populateTextAreaContextMenu: function(contextMenu, lineNumber)
+    {
+        WebInspector.SourceFrame.prototype.populateTextAreaContextMenu.call(this, contextMenu, lineNumber);
+        contextMenu.appendApplicableItems(this._uiSourceCode);
+        contextMenu.appendSeparator();
+    },
+
+    dispose: function()
+    {
+        this.detach();
+    },
+
+    __proto__: WebInspector.SourceFrame.prototype
+}
diff --git a/Source/devtools/front_end/UIString.js b/Source/devtools/front_end/UIString.js
new file mode 100644
index 0000000..d676f25
--- /dev/null
+++ b/Source/devtools/front_end/UIString.js
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 Google Inc.  All rights reserved.
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
+ * 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 Computer, 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.
+ */
+
+ /**
+ * @param {string} string
+ * @param {...*} vararg
+ * @return {string}
+ */
+WebInspector.UIString = function(string, vararg)
+{
+    return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));
+}
diff --git a/Source/devtools/front_end/UIUtils.js b/Source/devtools/front_end/UIUtils.js
new file mode 100644
index 0000000..d2b8637
--- /dev/null
+++ b/Source/devtools/front_end/UIUtils.js
@@ -0,0 +1,1091 @@
+/*
+ * Copyright (C) 2011 Google Inc.  All rights reserved.
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
+ * 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 Computer, 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.
+ */
+
+/**
+ * @param {Element} element
+ * @param {?function(Event): boolean} elementDragStart
+ * @param {function(Event)} elementDrag
+ * @param {?function(Event)} elementDragEnd
+ * @param {string} cursor
+ */
+WebInspector.installDragHandle = function(element, elementDragStart, elementDrag, elementDragEnd, cursor)
+{
+    element.addEventListener("mousedown", WebInspector._elementDragStart.bind(WebInspector, elementDragStart, elementDrag, elementDragEnd, cursor), false);
+}
+
+/**
+ * @param {?function(Event)} elementDragStart
+ * @param {function(Event)} elementDrag
+ * @param {?function(Event)} elementDragEnd
+ * @param {string} cursor
+ * @param {Event} event
+ */
+WebInspector._elementDragStart = function(elementDragStart, elementDrag, elementDragEnd, cursor, event)
+{
+    // Only drag upon left button. Right will likely cause a context menu. So will ctrl-click on mac.
+    if (event.button || (WebInspector.isMac() && event.ctrlKey))
+        return;
+
+    if (WebInspector._elementDraggingEventListener)
+        return;
+
+    if (elementDragStart && !elementDragStart(event))
+        return;
+
+    if (WebInspector._elementDraggingGlassPane) {
+        WebInspector._elementDraggingGlassPane.dispose();
+        delete WebInspector._elementDraggingGlassPane;
+    }
+
+    var targetDocument = event.target.ownerDocument;
+
+    WebInspector._elementDraggingEventListener = elementDrag;
+    WebInspector._elementEndDraggingEventListener = elementDragEnd;
+    WebInspector._mouseOutWhileDraggingTargetDocument = targetDocument;
+
+    targetDocument.addEventListener("mousemove", WebInspector._elementDragMove, true);
+    targetDocument.addEventListener("mouseup", WebInspector._elementDragEnd, true);
+    targetDocument.addEventListener("mouseout", WebInspector._mouseOutWhileDragging, true);
+
+    targetDocument.body.style.cursor = cursor;
+
+    event.preventDefault();
+}
+
+WebInspector._mouseOutWhileDragging = function()
+{
+    WebInspector._unregisterMouseOutWhileDragging();
+    WebInspector._elementDraggingGlassPane = new WebInspector.GlassPane();
+}
+
+WebInspector._unregisterMouseOutWhileDragging = function()
+{
+    if (!WebInspector._mouseOutWhileDraggingTargetDocument)
+        return;
+    WebInspector._mouseOutWhileDraggingTargetDocument.removeEventListener("mouseout", WebInspector._mouseOutWhileDragging, true);
+    delete WebInspector._mouseOutWhileDraggingTargetDocument;
+}
+
+WebInspector._elementDragMove = function(event)
+{
+    if (WebInspector._elementDraggingEventListener(event))
+        WebInspector._cancelDragEvents(event);
+}
+
+WebInspector._cancelDragEvents = function(event)
+{
+    var targetDocument = event.target.ownerDocument;
+    targetDocument.removeEventListener("mousemove", WebInspector._elementDragMove, true);
+    targetDocument.removeEventListener("mouseup", WebInspector._elementDragEnd, true);
+    WebInspector._unregisterMouseOutWhileDragging();
+
+    targetDocument.body.style.removeProperty("cursor");
+
+    if (WebInspector._elementDraggingGlassPane)
+        WebInspector._elementDraggingGlassPane.dispose();
+
+    delete WebInspector._elementDraggingGlassPane;
+    delete WebInspector._elementDraggingEventListener;
+    delete WebInspector._elementEndDraggingEventListener;
+}
+
+WebInspector._elementDragEnd = function(event)
+{
+    var elementDragEnd = WebInspector._elementEndDraggingEventListener;
+
+    WebInspector._cancelDragEvents(event);
+
+    event.preventDefault();
+    if (elementDragEnd)
+        elementDragEnd(event);
+}
+
+/**
+ * @constructor
+ */
+WebInspector.GlassPane = function()
+{
+    this.element = document.createElement("div");
+    this.element.style.cssText = "position:absolute;top:0;bottom:0;left:0;right:0;background-color:transparent;z-index:1000;";
+    this.element.id = "glass-pane-for-drag";
+    document.body.appendChild(this.element);
+    WebInspector._glassPane = this;
+}
+
+WebInspector.GlassPane.prototype = {
+    dispose: function()
+    {
+        delete WebInspector._glassPane;
+        WebInspector.inspectorView.focus();
+        if (this.element.parentElement)
+            this.element.parentElement.removeChild(this.element);
+    }
+}
+
+WebInspector.isBeingEdited = function(element)
+{
+    if (element.hasStyleClass("text-prompt") || element.nodeName === "INPUT" || element.nodeName === "TEXTAREA")
+        return true;
+
+    if (!WebInspector.__editingCount)
+        return false;
+
+    while (element) {
+        if (element.__editing)
+            return true;
+        element = element.parentElement;
+    }
+    return false;
+}
+
+WebInspector.markBeingEdited = function(element, value)
+{
+    if (value) {
+        if (element.__editing)
+            return false;
+        element.addStyleClass("being-edited");
+        element.__editing = true;
+        WebInspector.__editingCount = (WebInspector.__editingCount || 0) + 1;
+    } else {
+        if (!element.__editing)
+            return false;
+        element.removeStyleClass("being-edited");
+        delete element.__editing;
+        --WebInspector.__editingCount;
+    }
+    return true;
+}
+
+/**
+ * @constructor
+ * @param {function(Element,string,string,*,string)} commitHandler
+ * @param {function(Element,*)} cancelHandler
+ * @param {*=} context
+ */
+WebInspector.EditingConfig = function(commitHandler, cancelHandler, context)
+{
+    this.commitHandler = commitHandler;
+    this.cancelHandler = cancelHandler
+    this.context = context;
+
+    /**
+     * Handles the "paste" event, return values are the same as those for customFinishHandler
+     * @type {function(Element)|undefined}
+     */
+    this.pasteHandler;
+
+    /** 
+     * Whether the edited element is multiline
+     * @type {boolean|undefined}
+     */
+    this.multiline;
+
+    /**
+     * Custom finish handler for the editing session (invoked on keydown)
+     * @type {function(Element,*)|undefined}
+     */
+    this.customFinishHandler;
+}
+
+WebInspector.EditingConfig.prototype = {
+    setPasteHandler: function(pasteHandler)
+    {
+        this.pasteHandler = pasteHandler;
+    },
+
+    /**
+     * @param {string} initialValue
+     * @param {Object} mode
+     * @param {string} theme
+     * @param {boolean=} lineWrapping
+     * @param {boolean=} smartIndent
+     */
+    setMultilineOptions: function(initialValue, mode, theme, lineWrapping, smartIndent)
+    {
+        this.multiline = true;
+        this.initialValue = initialValue;
+        this.mode = mode;
+        this.theme = theme;
+        this.lineWrapping = lineWrapping;
+        this.smartIndent = smartIndent;
+    },
+
+    setCustomFinishHandler: function(customFinishHandler)
+    {
+        this.customFinishHandler = customFinishHandler;
+    }
+}
+
+WebInspector.CSSNumberRegex = /^(-?(?:\d+(?:\.\d+)?|\.\d+))$/;
+
+WebInspector.StyleValueDelimiters = " \xA0\t\n\"':;,/()";
+
+
+/**
+  * @param {Event} event
+  * @return {?string}
+  */
+WebInspector._valueModificationDirection = function(event)
+{
+    var direction = null;
+    if (event.type === "mousewheel") {
+        if (event.wheelDeltaY > 0)
+            direction = "Up";
+        else if (event.wheelDeltaY < 0)
+            direction = "Down";
+    } else {
+        if (event.keyIdentifier === "Up" || event.keyIdentifier === "PageUp")
+            direction = "Up";
+        else if (event.keyIdentifier === "Down" || event.keyIdentifier === "PageDown")
+            direction = "Down";        
+    }
+    return direction;
+}
+
+/**
+ * @param {string} hexString
+ * @param {Event} event
+ */
+WebInspector._modifiedHexValue = function(hexString, event)
+{
+    var direction = WebInspector._valueModificationDirection(event);
+    if (!direction)
+        return hexString;
+
+    var number = parseInt(hexString, 16);
+    if (isNaN(number) || !isFinite(number))
+        return hexString;
+
+    var maxValue = Math.pow(16, hexString.length) - 1;
+    var arrowKeyOrMouseWheelEvent = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down" || event.type === "mousewheel");
+    var delta;
+
+    if (arrowKeyOrMouseWheelEvent)
+        delta = (direction === "Up") ? 1 : -1;
+    else
+        delta = (event.keyIdentifier === "PageUp") ? 16 : -16;
+
+    if (event.shiftKey)
+        delta *= 16;
+
+    var result = number + delta;
+    if (result < 0)
+        result = 0; // Color hex values are never negative, so clamp to 0.
+    else if (result > maxValue)
+        return hexString;
+
+    // Ensure the result length is the same as the original hex value.
+    var resultString = result.toString(16).toUpperCase();
+    for (var i = 0, lengthDelta = hexString.length - resultString.length; i < lengthDelta; ++i)
+        resultString = "0" + resultString;
+    return resultString;
+}
+
+/**
+ * @param {number} number
+ * @param {Event} event
+ */
+WebInspector._modifiedFloatNumber = function(number, event)
+{
+    var direction = WebInspector._valueModificationDirection(event);
+    if (!direction)
+        return number;
+    
+    var arrowKeyOrMouseWheelEvent = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down" || event.type === "mousewheel");
+
+    // Jump by 10 when shift is down or jump by 0.1 when Alt/Option is down.
+    // Also jump by 10 for page up and down, or by 100 if shift is held with a page key.
+    var changeAmount = 1;
+    if (event.shiftKey && !arrowKeyOrMouseWheelEvent)
+        changeAmount = 100;
+    else if (event.shiftKey || !arrowKeyOrMouseWheelEvent)
+        changeAmount = 10;
+    else if (event.altKey)
+        changeAmount = 0.1;
+
+    if (direction === "Down")
+        changeAmount *= -1;
+
+    // Make the new number and constrain it to a precision of 6, this matches numbers the engine returns.
+    // Use the Number constructor to forget the fixed precision, so 1.100000 will print as 1.1.
+    var result = Number((number + changeAmount).toFixed(6));
+    if (!String(result).match(WebInspector.CSSNumberRegex))
+        return null;
+
+    return result;
+}
+
+/**
+  * @param {Event} event
+  * @param {Element} element
+  * @param {function(string,string)=} finishHandler
+  * @param {function(string)=} suggestionHandler
+  * @param {function(number):number=} customNumberHandler
+ */
+WebInspector.handleElementValueModifications = function(event, element, finishHandler, suggestionHandler, customNumberHandler)
+{
+    var arrowKeyOrMouseWheelEvent = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down" || event.type === "mousewheel");
+    var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdentifier === "PageDown");
+    if (!arrowKeyOrMouseWheelEvent && !pageKeyPressed)
+        return false;
+
+    var selection = window.getSelection();
+    if (!selection.rangeCount)
+        return false;
+
+    var selectionRange = selection.getRangeAt(0);
+    if (!selectionRange.commonAncestorContainer.isSelfOrDescendant(element))
+        return false;
+
+    var originalValue = element.textContent;
+    var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, WebInspector.StyleValueDelimiters, element);
+    var wordString = wordRange.toString();
+    
+    if (suggestionHandler && suggestionHandler(wordString))
+        return false;
+
+    var replacementString;
+    var prefix, suffix, number;
+
+    var matches;
+    matches = /(.*#)([\da-fA-F]+)(.*)/.exec(wordString);
+    if (matches && matches.length) {
+        prefix = matches[1];
+        suffix = matches[3];
+        number = WebInspector._modifiedHexValue(matches[2], event);
+        
+        if (customNumberHandler)
+            number = customNumberHandler(number);
+
+        replacementString = prefix + number + suffix;
+    } else {
+        matches = /(.*?)(-?(?:\d+(?:\.\d+)?|\.\d+))(.*)/.exec(wordString);
+        if (matches && matches.length) {
+            prefix = matches[1];
+            suffix = matches[3];
+            number = WebInspector._modifiedFloatNumber(parseFloat(matches[2]), event);
+            
+            // Need to check for null explicitly.
+            if (number === null)                
+                return false;
+            
+            if (customNumberHandler)
+                number = customNumberHandler(number);
+
+            replacementString = prefix + number + suffix;
+        }
+    }
+
+    if (replacementString) {
+        var replacementTextNode = document.createTextNode(replacementString);
+
+        wordRange.deleteContents();
+        wordRange.insertNode(replacementTextNode);
+
+        var finalSelectionRange = document.createRange();
+        finalSelectionRange.setStart(replacementTextNode, 0);
+        finalSelectionRange.setEnd(replacementTextNode, replacementString.length);
+
+        selection.removeAllRanges();
+        selection.addRange(finalSelectionRange);
+
+        event.handled = true;
+        event.preventDefault();
+                
+        if (finishHandler)
+            finishHandler(originalValue, replacementString);
+
+        return true;
+    }
+    return false;
+}
+
+/** 
+ * @param {Element} element
+ * @param {WebInspector.EditingConfig=} config
+ */
+WebInspector.startEditing = function(element, config)
+{
+    if (!WebInspector.markBeingEdited(element, true))
+        return null;
+
+    config = config || new WebInspector.EditingConfig(function() {}, function() {});
+    var committedCallback = config.commitHandler;
+    var cancelledCallback = config.cancelHandler;
+    var pasteCallback = config.pasteHandler;
+    var context = config.context;
+    var isMultiline = config.multiline || false;
+    var oldText = isMultiline ? config.initialValue : getContent(element);
+    var moveDirection = "";
+    var oldTabIndex;
+    var codeMirror;
+    var cssLoadView;
+
+    /**
+     * @param {Event} e
+     */
+    function consumeCopy(e)
+    {
+        e.consume();
+    }
+
+    if (isMultiline) {
+        loadScript("CodeMirrorTextEditor.js");
+        cssLoadView = new WebInspector.CodeMirrorCSSLoadView();
+        cssLoadView.show(element);
+        WebInspector.setCurrentFocusElement(element);
+        element.addEventListener("copy", consumeCopy, true);
+        codeMirror = window.CodeMirror(element, {
+            mode: config.mode,
+            lineWrapping: config.lineWrapping,
+            smartIndent: config.smartIndent,
+            autofocus: true,
+            theme: config.theme,
+            value: oldText
+        });
+        codeMirror.getWrapperElement().addStyleClass("source-code");
+    } else {
+        element.addStyleClass("editing");
+
+        oldTabIndex = element.getAttribute("tabIndex");
+        if (typeof oldTabIndex !== "number" || oldTabIndex < 0)
+            element.tabIndex = 0;
+        WebInspector.setCurrentFocusElement(element);
+    }
+
+    /**
+     * @param {Event=} e
+     */
+    function blurEventListener(e) {
+        if (!isMultiline || !e || !e.relatedTarget || !e.relatedTarget.isSelfOrDescendant(element))
+            editingCommitted.call(element);
+    }
+
+    function getContent(element) {
+        if (isMultiline)
+            return codeMirror.getValue();
+
+        if (element.tagName === "INPUT" && element.type === "text")
+            return element.value;
+
+        return element.textContent;
+    }
+
+    /** @this {Element} */
+    function cleanUpAfterEditing()
+    {
+        WebInspector.markBeingEdited(element, false);
+
+        element.removeEventListener("blur", blurEventListener, isMultiline);
+        element.removeEventListener("keydown", keyDownEventListener, true);
+        if (pasteCallback)
+            element.removeEventListener("paste", pasteEventListener, true);
+
+        WebInspector.restoreFocusFromElement(element);
+
+        if (isMultiline) {
+            element.removeEventListener("copy", consumeCopy, true);
+            cssLoadView.detach();
+            return;
+        }
+
+        this.removeStyleClass("editing");
+        
+        if (typeof oldTabIndex !== "number")
+            element.removeAttribute("tabIndex");
+        else
+            this.tabIndex = oldTabIndex;
+        this.scrollTop = 0;
+        this.scrollLeft = 0;
+    }
+
+    /** @this {Element} */
+    function editingCancelled()
+    {
+        if (isMultiline)
+            codeMirror.setValue(oldText);
+        else {
+            if (this.tagName === "INPUT" && this.type === "text")
+                this.value = oldText;
+            else
+                this.textContent = oldText;
+        }
+
+        cleanUpAfterEditing.call(this);
+
+        cancelledCallback(this, context);
+    }
+
+    /** @this {Element} */
+    function editingCommitted()
+    {
+        cleanUpAfterEditing.call(this);
+
+        committedCallback(this, getContent(this), oldText, context, moveDirection);
+    }
+
+    function defaultFinishHandler(event)
+    {
+        var isMetaOrCtrl = WebInspector.isMac() ?
+            event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey :
+            event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
+        if (isEnterKey(event) && (event.isMetaOrCtrlForTest || !isMultiline || isMetaOrCtrl))
+            return "commit";
+        else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
+            return "cancel";
+        else if (!isMultiline && event.keyIdentifier === "U+0009") // Tab key
+            return "move-" + (event.shiftKey ? "backward" : "forward");
+    }
+
+    function handleEditingResult(result, event)
+    {
+        if (result === "commit") {
+            editingCommitted.call(element);
+            event.consume(true);
+        } else if (result === "cancel") {
+            editingCancelled.call(element);
+            event.consume(true);
+        } else if (result && result.startsWith("move-")) {
+            moveDirection = result.substring(5);
+            if (event.keyIdentifier !== "U+0009")
+                blurEventListener();
+        }
+    }
+
+    function pasteEventListener(event)
+    {
+        var result = pasteCallback(event);
+        handleEditingResult(result, event);
+    }
+
+    function keyDownEventListener(event)
+    {
+        var handler = config.customFinishHandler || defaultFinishHandler;
+        var result = handler(event);
+        handleEditingResult(result, event);
+    }
+
+    element.addEventListener("blur", blurEventListener, isMultiline);
+    element.addEventListener("keydown", keyDownEventListener, true);
+    if (pasteCallback)
+        element.addEventListener("paste", pasteEventListener, true);
+
+    return {
+        cancel: editingCancelled.bind(element),
+        commit: editingCommitted.bind(element),
+        codeMirror: codeMirror // For testing.
+    };
+}
+
+/**
+ * @param {number} seconds
+ * @param {boolean=} higherResolution
+ * @return {string}
+ */
+Number.secondsToString = function(seconds, higherResolution)
+{
+    if (!isFinite(seconds))
+        return "-";
+
+    if (seconds === 0)
+        return "0";
+
+    var ms = seconds * 1000;
+    if (higherResolution && ms < 1000)
+        return WebInspector.UIString("%.3f\u2009ms", ms);
+    else if (ms < 1000)
+        return WebInspector.UIString("%.0f\u2009ms", ms);
+
+    if (seconds < 60)
+        return WebInspector.UIString("%.2f\u2009s", seconds);
+
+    var minutes = seconds / 60;
+    if (minutes < 60)
+        return WebInspector.UIString("%.1f\u2009min", minutes);
+
+    var hours = minutes / 60;
+    if (hours < 24)
+        return WebInspector.UIString("%.1f\u2009hrs", hours);
+
+    var days = hours / 24;
+    return WebInspector.UIString("%.1f\u2009days", days);
+}
+
+/**
+ * @param {number} bytes
+ * @return {string}
+ */
+Number.bytesToString = function(bytes)
+{
+    if (bytes < 1024)
+        return WebInspector.UIString("%.0f\u2009B", bytes);
+
+    var kilobytes = bytes / 1024;
+    if (kilobytes < 100)
+        return WebInspector.UIString("%.1f\u2009KB", kilobytes);
+    if (kilobytes < 1024)
+        return WebInspector.UIString("%.0f\u2009KB", kilobytes);
+
+    var megabytes = kilobytes / 1024;
+    if (megabytes < 100)
+        return WebInspector.UIString("%.1f\u2009MB", megabytes);
+    else
+        return WebInspector.UIString("%.0f\u2009MB", megabytes);
+}
+
+Number.withThousandsSeparator = function(num)
+{
+    var str = num + "";
+    var re = /(\d+)(\d{3})/;
+    while (str.match(re))
+        str = str.replace(re, "$1\u2009$2"); // \u2009 is a thin space.
+    return str;
+}
+
+WebInspector.useLowerCaseMenuTitles = function()
+{
+    return WebInspector.platform() === "windows";
+}
+
+WebInspector.formatLocalized = function(format, substitutions, formatters, initialValue, append)
+{
+    return String.format(WebInspector.UIString(format), substitutions, formatters, initialValue, append);
+}
+
+WebInspector.openLinkExternallyLabel = function()
+{
+    return WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open link in new tab" : "Open Link in New Tab");
+}
+
+WebInspector.copyLinkAddressLabel = function()
+{
+    return WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy link address" : "Copy Link Address");
+}
+
+WebInspector.platform = function()
+{
+    if (!WebInspector._platform)
+        WebInspector._platform = InspectorFrontendHost.platform();
+    return WebInspector._platform;
+}
+
+WebInspector.isMac = function()
+{
+    if (typeof WebInspector._isMac === "undefined")
+        WebInspector._isMac = WebInspector.platform() === "mac";
+
+    return WebInspector._isMac;
+}
+
+WebInspector.isWin = function()
+{
+    if (typeof WebInspector._isWin === "undefined")
+        WebInspector._isWin = WebInspector.platform() === "windows";
+
+    return WebInspector._isWin;
+}
+
+WebInspector.PlatformFlavor = {
+    WindowsVista: "windows-vista",
+    MacTiger: "mac-tiger",
+    MacLeopard: "mac-leopard",
+    MacSnowLeopard: "mac-snowleopard",
+    MacLion: "mac-lion",
+    MacMountainLion: "mac-mountain-lion"
+}
+
+WebInspector.platformFlavor = function()
+{
+    function detectFlavor()
+    {
+        const userAgent = navigator.userAgent;
+
+        if (WebInspector.platform() === "windows") {
+            var match = userAgent.match(/Windows NT (\d+)\.(?:\d+)/);
+            if (match && match[1] >= 6)
+                return WebInspector.PlatformFlavor.WindowsVista;
+            return null;
+        } else if (WebInspector.platform() === "mac") {
+            var match = userAgent.match(/Mac OS X\s*(?:(\d+)_(\d+))?/);
+            if (!match || match[1] != 10)
+                return WebInspector.PlatformFlavor.MacSnowLeopard;
+            switch (Number(match[2])) {
+                case 4:
+                    return WebInspector.PlatformFlavor.MacTiger;
+                case 5:
+                    return WebInspector.PlatformFlavor.MacLeopard;
+                case 6:
+                    return WebInspector.PlatformFlavor.MacSnowLeopard;
+                case 7:
+                    return WebInspector.PlatformFlavor.MacLion;
+                case 8:
+                    return WebInspector.PlatformFlavor.MacMountainLion;
+                default:
+                    return "";
+            }
+        }
+    }
+
+    if (!WebInspector._platformFlavor)
+        WebInspector._platformFlavor = detectFlavor();
+
+    return WebInspector._platformFlavor;
+}
+
+WebInspector.port = function()
+{
+    if (!WebInspector._port)
+        WebInspector._port = InspectorFrontendHost.port();
+
+    return WebInspector._port;
+}
+
+WebInspector.installPortStyles = function()
+{
+    var platform = WebInspector.platform();
+    document.body.addStyleClass("platform-" + platform);
+    var flavor = WebInspector.platformFlavor();
+    if (flavor)
+        document.body.addStyleClass("platform-" + flavor);
+    var port = WebInspector.port();
+    document.body.addStyleClass("port-" + port);
+}
+
+WebInspector._windowFocused = function(event)
+{
+    if (event.target.document.nodeType === Node.DOCUMENT_NODE)
+        document.body.removeStyleClass("inactive");
+}
+
+WebInspector._windowBlurred = function(event)
+{
+    if (event.target.document.nodeType === Node.DOCUMENT_NODE)
+        document.body.addStyleClass("inactive");
+}
+
+WebInspector.previousFocusElement = function()
+{
+    return WebInspector._previousFocusElement;
+}
+
+WebInspector.currentFocusElement = function()
+{
+    return WebInspector._currentFocusElement;
+}
+
+WebInspector._focusChanged = function(event)
+{
+    WebInspector.setCurrentFocusElement(event.target);
+}
+
+WebInspector._textInputTypes = ["text", "search", "tel", "url", "email", "password"].keySet(); 
+WebInspector._isTextEditingElement = function(element)
+{
+    if (element instanceof HTMLInputElement)
+        return element.type in WebInspector._textInputTypes;
+
+    if (element instanceof HTMLTextAreaElement)
+        return true;
+
+    return false;
+}
+
+WebInspector.setCurrentFocusElement = function(x)
+{
+    if (WebInspector._glassPane && x && !WebInspector._glassPane.element.isAncestor(x))
+        return;
+    if (WebInspector._currentFocusElement !== x)
+        WebInspector._previousFocusElement = WebInspector._currentFocusElement;
+    WebInspector._currentFocusElement = x;
+
+    if (WebInspector._currentFocusElement) {
+        WebInspector._currentFocusElement.focus();
+
+        // Make a caret selection inside the new element if there isn't a range selection and there isn't already a caret selection inside.
+        // This is needed (at least) to remove caret from console when focus is moved to some element in the panel.
+        // The code below should not be applied to text fields and text areas, hence _isTextEditingElement check.
+        var selection = window.getSelection();
+        if (!WebInspector._isTextEditingElement(WebInspector._currentFocusElement) && selection.isCollapsed && !WebInspector._currentFocusElement.isInsertionCaretInside()) {
+            var selectionRange = WebInspector._currentFocusElement.ownerDocument.createRange();
+            selectionRange.setStart(WebInspector._currentFocusElement, 0);
+            selectionRange.setEnd(WebInspector._currentFocusElement, 0);
+
+            selection.removeAllRanges();
+            selection.addRange(selectionRange);
+        }
+    } else if (WebInspector._previousFocusElement)
+        WebInspector._previousFocusElement.blur();
+}
+
+WebInspector.restoreFocusFromElement = function(element)
+{
+    if (element && element.isSelfOrAncestor(WebInspector.currentFocusElement()))
+        WebInspector.setCurrentFocusElement(WebInspector.previousFocusElement());
+}
+
+WebInspector.setToolbarColors = function(backgroundColor, color)
+{
+    if (!WebInspector._themeStyleElement) {
+        WebInspector._themeStyleElement = document.createElement("style");
+        document.head.appendChild(WebInspector._themeStyleElement);
+    }
+    WebInspector._themeStyleElement.textContent =
+        "#toolbar {\
+             background-image: none !important;\
+             background-color: " + backgroundColor + " !important;\
+         }\
+         \
+         .toolbar-label {\
+             color: " + color + " !important;\
+             text-shadow: none;\
+         }";
+}
+
+WebInspector.resetToolbarColors = function()
+{
+    if (WebInspector._themeStyleElement)
+        WebInspector._themeStyleElement.textContent = "";
+}
+
+/**
+ * @param {Element} element
+ * @param {number} offset
+ * @param {number} length
+ * @param {Array.<Object>=} domChanges
+ */
+WebInspector.highlightSearchResult = function(element, offset, length, domChanges)
+{
+    var result = WebInspector.highlightSearchResults(element, [{offset: offset, length: length }], domChanges);
+    return result.length ? result[0] : null;
+}
+
+/**
+ * @param {Element} element
+ * @param {Array.<Object>} resultRanges
+ * @param {Array.<Object>=} changes
+ */
+WebInspector.highlightSearchResults = function(element, resultRanges, changes)
+{
+    return WebInspector.highlightRangesWithStyleClass(element, resultRanges, "webkit-search-result", changes);
+}
+
+/**
+ * @param {Element} element
+ * @param {Array.<Object>} resultRanges
+ * @param {string} styleClass
+ * @param {Array.<Object>=} changes
+ */
+WebInspector.highlightRangesWithStyleClass = function(element, resultRanges, styleClass, changes)
+{
+    changes = changes || [];
+    var highlightNodes = [];
+    var lineText = element.textContent;
+    var ownerDocument = element.ownerDocument;
+    var textNodeSnapshot = ownerDocument.evaluate(".//text()", element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+
+    var snapshotLength = textNodeSnapshot.snapshotLength;
+    if (snapshotLength === 0)
+        return highlightNodes;
+
+    var nodeRanges = [];
+    var rangeEndOffset = 0;
+    for (var i = 0; i < snapshotLength; ++i) {
+        var range = {};
+        range.offset = rangeEndOffset;
+        range.length = textNodeSnapshot.snapshotItem(i).textContent.length;
+        rangeEndOffset = range.offset + range.length;
+        nodeRanges.push(range);
+    }
+
+    var startIndex = 0;
+    for (var i = 0; i < resultRanges.length; ++i) {
+        var startOffset = resultRanges[i].offset;
+        var endOffset = startOffset + resultRanges[i].length;
+
+        while (startIndex < snapshotLength && nodeRanges[startIndex].offset + nodeRanges[startIndex].length <= startOffset)
+            startIndex++;
+        var endIndex = startIndex;
+        while (endIndex < snapshotLength && nodeRanges[endIndex].offset + nodeRanges[endIndex].length < endOffset)
+            endIndex++;
+        if (endIndex === snapshotLength)
+            break;
+
+        var highlightNode = ownerDocument.createElement("span");
+        highlightNode.className = styleClass;
+        highlightNode.textContent = lineText.substring(startOffset, endOffset);
+
+        var lastTextNode = textNodeSnapshot.snapshotItem(endIndex);
+        var lastText = lastTextNode.textContent;
+        lastTextNode.textContent = lastText.substring(endOffset - nodeRanges[endIndex].offset);
+        changes.push({ node: lastTextNode, type: "changed", oldText: lastText, newText: lastTextNode.textContent });
+
+        if (startIndex === endIndex) {
+            lastTextNode.parentElement.insertBefore(highlightNode, lastTextNode);
+            changes.push({ node: highlightNode, type: "added", nextSibling: lastTextNode, parent: lastTextNode.parentElement });
+            highlightNodes.push(highlightNode);
+
+            var prefixNode = ownerDocument.createTextNode(lastText.substring(0, startOffset - nodeRanges[startIndex].offset));
+            lastTextNode.parentElement.insertBefore(prefixNode, highlightNode);
+            changes.push({ node: prefixNode, type: "added", nextSibling: highlightNode, parent: lastTextNode.parentElement });
+        } else {
+            var firstTextNode = textNodeSnapshot.snapshotItem(startIndex);
+            var firstText = firstTextNode.textContent;
+            var anchorElement = firstTextNode.nextSibling;
+
+            firstTextNode.parentElement.insertBefore(highlightNode, anchorElement);
+            changes.push({ node: highlightNode, type: "added", nextSibling: anchorElement, parent: firstTextNode.parentElement });
+            highlightNodes.push(highlightNode);
+
+            firstTextNode.textContent = firstText.substring(0, startOffset - nodeRanges[startIndex].offset);
+            changes.push({ node: firstTextNode, type: "changed", oldText: firstText, newText: firstTextNode.textContent });
+
+            for (var j = startIndex + 1; j < endIndex; j++) {
+                var textNode = textNodeSnapshot.snapshotItem(j);
+                var text = textNode.textContent;
+                textNode.textContent = "";
+                changes.push({ node: textNode, type: "changed", oldText: text, newText: textNode.textContent });
+            }
+        }
+        startIndex = endIndex;
+        nodeRanges[startIndex].offset = endOffset;
+        nodeRanges[startIndex].length = lastTextNode.textContent.length;
+
+    }
+    return highlightNodes;
+}
+
+WebInspector.applyDomChanges = function(domChanges)
+{
+    for (var i = 0, size = domChanges.length; i < size; ++i) {
+        var entry = domChanges[i];
+        switch (entry.type) {
+        case "added":
+            entry.parent.insertBefore(entry.node, entry.nextSibling);
+            break;
+        case "changed":
+            entry.node.textContent = entry.newText;
+            break;
+        }
+    }
+}
+
+WebInspector.revertDomChanges = function(domChanges)
+{
+    for (var i = domChanges.length - 1; i >= 0; --i) {
+        var entry = domChanges[i];
+        switch (entry.type) {
+        case "added":
+            if (entry.node.parentElement)
+                entry.node.parentElement.removeChild(entry.node);
+            break;
+        case "changed":
+            entry.node.textContent = entry.oldText;
+            break;
+        }
+    }
+}
+
+WebInspector._coalescingLevel = 0;
+
+WebInspector.startBatchUpdate = function()
+{
+    if (!WebInspector._coalescingLevel)
+        WebInspector._postUpdateHandlers = new Map();
+    WebInspector._coalescingLevel++;
+}
+
+WebInspector.endBatchUpdate = function()
+{
+    if (--WebInspector._coalescingLevel)
+        return;
+
+    var handlers = WebInspector._postUpdateHandlers;
+    delete WebInspector._postUpdateHandlers;
+
+    var keys = handlers.keys();
+    for (var i = 0; i < keys.length; ++i) {
+        var object = keys[i];
+        var methods = handlers.get(object).keys();
+        for (var j = 0; j < methods.length; ++j)
+            methods[j].call(object);
+    }
+}
+
+/**
+ * @param {Object} object
+ * @param {function()} method
+ */
+WebInspector.invokeOnceAfterBatchUpdate = function(object, method)
+{
+    if (!WebInspector._coalescingLevel) {
+        method.call(object);
+        return;
+    }
+    
+    var methods = WebInspector._postUpdateHandlers.get(object);
+    if (!methods) {
+        methods = new Map();
+        WebInspector._postUpdateHandlers.put(object, methods);
+    }
+    methods.put(method);
+}
+
+/**
+ * This bogus view is needed to load/unload CodeMirror-related CSS on demand.
+ *
+ * @constructor
+ * @extends {WebInspector.View}
+ */
+WebInspector.CodeMirrorCSSLoadView = function()
+{
+    WebInspector.View.call(this);
+    this.element.addStyleClass("hidden");
+    this.registerRequiredCSS("cm/codemirror.css");
+    this.registerRequiredCSS("cm/cmdevtools.css");
+}
+
+WebInspector.CodeMirrorCSSLoadView.prototype = {
+    __proto__: WebInspector.View.prototype
+}
+
+;(function() {
+
+function windowLoaded()
+{
+    window.addEventListener("focus", WebInspector._windowFocused, false);
+    window.addEventListener("blur", WebInspector._windowBlurred, false);
+    document.addEventListener("focus", WebInspector._focusChanged.bind(this), true);
+    window.removeEventListener("DOMContentLoaded", windowLoaded, false);
+}
+
+window.addEventListener("DOMContentLoaded", windowLoaded, false);
+
+})();
diff --git a/Source/devtools/front_end/UglifyJS/parse-js.js b/Source/devtools/front_end/UglifyJS/parse-js.js
new file mode 100644
index 0000000..a559145
--- /dev/null
+++ b/Source/devtools/front_end/UglifyJS/parse-js.js
@@ -0,0 +1,1247 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+
+  This version is suitable for Node.js.  With minimal changes (the
+  exports stuff) it should work on any JS platform.
+
+  This file contains the tokenizer/parser.  It is a port to JavaScript
+  of parse-js [1], a JavaScript parser library written in Common Lisp
+  by Marijn Haverbeke.  Thank you Marijn!
+
+  [1] http://marijn.haverbeke.nl/parse-js/
+
+  Exported functions:
+
+    - tokenizer(code) -- returns a function.  Call the returned
+      function to fetch the next token.
+
+    - parse(code) -- returns an AST of the given JavaScript code.
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
+    Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
+
+    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.
+
+    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.
+
+ ***********************************************************************/
+
+/* -----[ Tokenizer (constants) ]----- */
+
+var KEYWORDS = array_to_hash([
+        "break",
+        "case",
+        "catch",
+        "const",
+        "continue",
+        "default",
+        "delete",
+        "do",
+        "else",
+        "finally",
+        "for",
+        "function",
+        "if",
+        "in",
+        "instanceof",
+        "new",
+        "return",
+        "switch",
+        "throw",
+        "try",
+        "typeof",
+        "var",
+        "void",
+        "while",
+        "with"
+]);
+
+var RESERVED_WORDS = array_to_hash([
+        "abstract",
+        "boolean",
+        "byte",
+        "char",
+        "class",
+        "debugger",
+        "double",
+        "enum",
+        "export",
+        "extends",
+        "final",
+        "float",
+        "goto",
+        "implements",
+        "import",
+        "int",
+        "interface",
+        "long",
+        "native",
+        "package",
+        "private",
+        "protected",
+        "public",
+        "short",
+        "static",
+        "super",
+        "synchronized",
+        "throws",
+        "transient",
+        "volatile"
+]);
+
+var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([
+        "return",
+        "new",
+        "delete",
+        "throw",
+        "else",
+        "case"
+]);
+
+var KEYWORDS_ATOM = array_to_hash([
+        "false",
+        "null",
+        "true",
+        "undefined"
+]);
+
+var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^"));
+
+var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
+var RE_OCT_NUMBER = /^0[0-7]+$/;
+var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
+
+var OPERATORS = array_to_hash([
+        "in",
+        "instanceof",
+        "typeof",
+        "new",
+        "void",
+        "delete",
+        "++",
+        "--",
+        "+",
+        "-",
+        "!",
+        "~",
+        "&",
+        "|",
+        "^",
+        "*",
+        "/",
+        "%",
+        ">>",
+        "<<",
+        ">>>",
+        "<",
+        ">",
+        "<=",
+        ">=",
+        "==",
+        "===",
+        "!=",
+        "!==",
+        "?",
+        "=",
+        "+=",
+        "-=",
+        "/=",
+        "*=",
+        "%=",
+        ">>=",
+        "<<=",
+        ">>>=",
+        "%=",
+        "|=",
+        "^=",
+        "&=",
+        "&&",
+        "||"
+]);
+
+var WHITESPACE_CHARS = array_to_hash(characters(" \n\r\t"));
+
+var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:"));
+
+var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
+
+var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy"));
+
+/* -----[ Tokenizer ]----- */
+
+function is_alphanumeric_char(ch) {
+        ch = ch.charCodeAt(0);
+        return (ch >= 48 && ch <= 57) ||
+                (ch >= 65 && ch <= 90) ||
+                (ch >= 97 && ch <= 122);
+};
+
+function is_identifier_char(ch) {
+        return is_alphanumeric_char(ch) || ch == "$" || ch == "_";
+};
+
+function is_digit(ch) {
+        ch = ch.charCodeAt(0);
+        return ch >= 48 && ch <= 57;
+};
+
+function parse_js_number(num) {
+        if (RE_HEX_NUMBER.test(num)) {
+                return parseInt(num.substr(2), 16);
+        } else if (RE_OCT_NUMBER.test(num)) {
+                return parseInt(num.substr(1), 8);
+        } else if (RE_DEC_NUMBER.test(num)) {
+                return parseFloat(num);
+        }
+};
+
+function JS_Parse_Error(message, line, col, pos) {
+        this.message = message;
+        this.line = line;
+        this.col = col;
+        this.pos = pos;
+        try {
+                ({})();
+        } catch(ex) {
+                this.stack = ex.stack;
+        };
+};
+
+JS_Parse_Error.prototype.toString = function() {
+        return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
+};
+
+function js_error(message, line, col, pos) {
+        throw new JS_Parse_Error(message, line, col, pos);
+};
+
+function is_token(token, type, val) {
+        return token.type == type && (val == null || token.value == val);
+};
+
+var EX_EOF = {};
+
+function tokenizer($TEXT) {
+
+        var S = {
+                text            : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
+                pos             : 0,
+                tokpos          : 0,
+                line            : 0,
+                tokline         : 0,
+                col             : 0,
+                tokcol          : 0,
+                newline_before  : false,
+                regex_allowed   : false,
+                comments_before : []
+        };
+
+        function peek() { return S.text.charAt(S.pos); };
+
+        function next(signal_eof) {
+                var ch = S.text.charAt(S.pos++);
+                if (signal_eof && !ch)
+                        throw EX_EOF;
+                if (ch == "\n") {
+                        S.newline_before = true;
+                        ++S.line;
+                        S.col = 0;
+                } else {
+                        ++S.col;
+                }
+                return ch;
+        };
+
+        function eof() {
+                return !S.peek();
+        };
+
+        function find(what, signal_eof) {
+                var pos = S.text.indexOf(what, S.pos);
+                if (signal_eof && pos == -1) throw EX_EOF;
+                return pos;
+        };
+
+        function start_token() {
+                S.tokline = S.line;
+                S.tokcol = S.col;
+                S.tokpos = S.pos;
+        };
+
+        function token(type, value, is_comment) {
+                S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
+                                   (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
+                                   (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
+                var ret = {
+                        type  : type,
+                        value : value,
+                        line  : S.tokline,
+                        col   : S.tokcol,
+                        pos   : S.tokpos,
+                        nlb   : S.newline_before
+                };
+                if (!is_comment) {
+                        ret.comments_before = S.comments_before;
+                        S.comments_before = [];
+                }
+                S.newline_before = false;
+                return ret;
+        };
+
+        function skip_whitespace() {
+                while (HOP(WHITESPACE_CHARS, peek()))
+                        next();
+        };
+
+        function read_while(pred) {
+                var ret = "", ch = peek(), i = 0;
+                while (ch && pred(ch, i++)) {
+                        ret += next();
+                        ch = peek();
+                }
+                return ret;
+        };
+
+        function parse_error(err) {
+                js_error(err, S.tokline, S.tokcol, S.tokpos);
+        };
+
+        function read_num(prefix) {
+                var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
+                var num = read_while(function(ch, i){
+                        if (ch == "x" || ch == "X") {
+                                if (has_x) return false;
+                                return has_x = true;
+                        }
+                        if (!has_x && (ch == "E" || ch == "e")) {
+                                if (has_e) return false;
+                                return has_e = after_e = true;
+                        }
+                        if (ch == "-") {
+                                if (after_e || (i == 0 && !prefix)) return true;
+                                return false;
+                        }
+                        if (ch == "+") return after_e;
+                        after_e = false;
+                        if (ch == ".") {
+                                if (!has_dot)
+                                        return has_dot = true;
+                                return false;
+                        }
+                        return is_alphanumeric_char(ch);
+                });
+                if (prefix)
+                        num = prefix + num;
+                var valid = parse_js_number(num);
+                if (!isNaN(valid)) {
+                        return token("num", valid);
+                } else {
+                        parse_error("Invalid syntax: " + num);
+                }
+        };
+
+        function read_escaped_char() {
+                var ch = next(true);
+                switch (ch) {
+                    case "n" : return "\n";
+                    case "r" : return "\r";
+                    case "t" : return "\t";
+                    case "b" : return "\b";
+                    case "v" : return "\v";
+                    case "f" : return "\f";
+                    case "0" : return "\0";
+                    case "x" : return String.fromCharCode(hex_bytes(2));
+                    case "u" : return String.fromCharCode(hex_bytes(4));
+                    default  : return ch;
+                }
+        };
+
+        function hex_bytes(n) {
+                var num = 0;
+                for (; n > 0; --n) {
+                        var digit = parseInt(next(true), 16);
+                        if (isNaN(digit))
+                                parse_error("Invalid hex-character pattern in string");
+                        num = (num << 4) | digit;
+                }
+                return num;
+        };
+
+        function read_string() {
+                return with_eof_error("Unterminated string constant", function(){
+                        var quote = next(), ret = "";
+                        for (;;) {
+                                var ch = next(true);
+                                if (ch == "\\") ch = read_escaped_char();
+                                else if (ch == quote) break;
+                                ret += ch;
+                        }
+                        return token("string", ret);
+                });
+        };
+
+        function read_line_comment() {
+                next();
+                var i = find("\n"), ret;
+                if (i == -1) {
+                        ret = S.text.substr(S.pos);
+                        S.pos = S.text.length;
+                } else {
+                        ret = S.text.substring(S.pos, i);
+                        S.pos = i;
+                }
+                return token("comment1", ret, true);
+        };
+
+        function read_multiline_comment() {
+                next();
+                return with_eof_error("Unterminated multiline comment", function(){
+                        var i = find("*/", true),
+                            text = S.text.substring(S.pos, i),
+                            tok = token("comment2", text, true);
+                        S.pos = i + 2;
+                        S.line += text.split("\n").length - 1;
+                        S.newline_before = text.indexOf("\n") >= 0;
+                        return tok;
+                });
+        };
+
+        function read_regexp() {
+                return with_eof_error("Unterminated regular expression", function(){
+                        var prev_backslash = false, regexp = "", ch, in_class = false;
+                        while ((ch = next(true))) if (prev_backslash) {
+                                regexp += "\\" + ch;
+                                prev_backslash = false;
+                        } else if (ch == "[") {
+                                in_class = true;
+                                regexp += ch;
+                        } else if (ch == "]" && in_class) {
+                                in_class = false;
+                                regexp += ch;
+                        } else if (ch == "/" && !in_class) {
+                                break;
+                        } else if (ch == "\\") {
+                                prev_backslash = true;
+                        } else {
+                                regexp += ch;
+                        }
+                        var mods = read_while(function(ch){
+                                return HOP(REGEXP_MODIFIERS, ch);
+                        });
+                        return token("regexp", [ regexp, mods ]);
+                });
+        };
+
+        function read_operator(prefix) {
+                function grow(op) {
+                        if (!peek()) return op;
+                        var bigger = op + peek();
+                        if (HOP(OPERATORS, bigger)) {
+                                next();
+                                return grow(bigger);
+                        } else {
+                                return op;
+                        }
+                };
+                return token("operator", grow(prefix || next()));
+        };
+
+        function handle_slash() {
+                next();
+                var regex_allowed = S.regex_allowed;
+                switch (peek()) {
+                    case "/":
+                        S.comments_before.push(read_line_comment());
+                        S.regex_allowed = regex_allowed;
+                        return next_token();
+                    case "*":
+                        S.comments_before.push(read_multiline_comment());
+                        S.regex_allowed = regex_allowed;
+                        return next_token();
+                }
+                return S.regex_allowed ? read_regexp() : read_operator("/");
+        };
+
+        function handle_dot() {
+                next();
+                return is_digit(peek())
+                        ? read_num(".")
+                        : token("punc", ".");
+        };
+
+        function read_word() {
+                var word = read_while(is_identifier_char);
+                return !HOP(KEYWORDS, word)
+                        ? token("name", word)
+                        : HOP(OPERATORS, word)
+                        ? token("operator", word)
+                        : HOP(KEYWORDS_ATOM, word)
+                        ? token("atom", word)
+                        : token("keyword", word);
+        };
+
+        function with_eof_error(eof_error, cont) {
+                try {
+                        return cont();
+                } catch(ex) {
+                        if (ex === EX_EOF) parse_error(eof_error);
+                        else throw ex;
+                }
+        };
+
+        function next_token(force_regexp) {
+                if (force_regexp)
+                        return read_regexp();
+                skip_whitespace();
+                start_token();
+                var ch = peek();
+                if (!ch) return token("eof");
+                if (is_digit(ch)) return read_num();
+                if (ch == '"' || ch == "'") return read_string();
+                if (HOP(PUNC_CHARS, ch)) return token("punc", next());
+                if (ch == ".") return handle_dot();
+                if (ch == "/") return handle_slash();
+                if (HOP(OPERATOR_CHARS, ch)) return read_operator();
+                if (is_identifier_char(ch)) return read_word();
+                parse_error("Unexpected character '" + ch + "'");
+        };
+
+        next_token.context = function(nc) {
+                if (nc) S = nc;
+                return S;
+        };
+
+        return next_token;
+
+};
+
+/* -----[ Parser (constants) ]----- */
+
+var UNARY_PREFIX = array_to_hash([
+        "typeof",
+        "void",
+        "delete",
+        "--",
+        "++",
+        "!",
+        "~",
+        "-",
+        "+"
+]);
+
+var UNARY_POSTFIX = array_to_hash([ "--", "++" ]);
+
+var ASSIGNMENT = (function(a, ret, i){
+        while (i < a.length) {
+                ret[a[i]] = a[i].substr(0, a[i].length - 1);
+                i++;
+        }
+        return ret;
+})(
+        ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="],
+        { "=": true },
+        0
+);
+
+var PRECEDENCE = (function(a, ret){
+        for (var i = 0, n = 1; i < a.length; ++i, ++n) {
+                var b = a[i];
+                for (var j = 0; j < b.length; ++j) {
+                        ret[b[j]] = n;
+                }
+        }
+        return ret;
+})(
+        [
+                ["||"],
+                ["&&"],
+                ["|"],
+                ["^"],
+                ["&"],
+                ["==", "===", "!=", "!=="],
+                ["<", ">", "<=", ">=", "in", "instanceof"],
+                [">>", "<<", ">>>"],
+                ["+", "-"],
+                ["*", "/", "%"]
+        ],
+        {}
+);
+
+var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
+
+var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
+
+/* -----[ Parser ]----- */
+
+function NodeWithToken(str, start, end) {
+        this.name = str;
+        this.start = start;
+        this.end = end;
+};
+
+NodeWithToken.prototype.toString = function() { return this.name; };
+
+function parse($TEXT, strict_mode, embed_tokens) {
+
+        var S = {
+                input       : typeof $TEXT == "string" ? tokenizer($TEXT, true) : $TEXT,
+                token       : null,
+                prev        : null,
+                peeked      : null,
+                in_function : 0,
+                in_loop     : 0,
+                labels      : []
+        };
+
+        S.token = next();
+
+        function is(type, value) {
+                return is_token(S.token, type, value);
+        };
+
+        function peek() { return S.peeked || (S.peeked = S.input()); };
+
+        function next() {
+                S.prev = S.token;
+                if (S.peeked) {
+                        S.token = S.peeked;
+                        S.peeked = null;
+                } else {
+                        S.token = S.input();
+                }
+                return S.token;
+        };
+
+        function prev() {
+                return S.prev;
+        };
+
+        function croak(msg, line, col, pos) {
+                var ctx = S.input.context();
+                js_error(msg,
+                         line != null ? line : ctx.tokline,
+                         col != null ? col : ctx.tokcol,
+                         pos != null ? pos : ctx.tokpos);
+        };
+
+        function token_error(token, msg) {
+                croak(msg, token.line, token.col);
+        };
+
+        function unexpected(token) {
+                if (token == null)
+                        token = S.token;
+                token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
+        };
+
+        function expect_token(type, val) {
+                if (is(type, val)) {
+                        return next();
+                }
+                token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type);
+        };
+
+        function expect(punc) { return expect_token("punc", punc); };
+
+        function can_insert_semicolon() {
+                return !strict_mode && (
+                        S.token.nlb || is("eof") || is("punc", "}")
+                );
+        };
+
+        function semicolon() {
+                if (is("punc", ";")) next();
+                else if (!can_insert_semicolon()) unexpected();
+        };
+
+        function as() {
+                return slice(arguments);
+        };
+
+        function parenthesised() {
+                expect("(");
+                var ex = expression();
+                expect(")");
+                return ex;
+        };
+
+        function add_tokens(str, start, end) {
+                return new NodeWithToken(str, start, end);
+        };
+
+        var statement = embed_tokens ? function() {
+                var start = S.token;
+                var stmt = $statement();
+                stmt[0] = add_tokens(stmt[0], start, prev());
+                return stmt;
+        } : $statement;
+
+        function $statement() {
+                if (is("operator", "/")) {
+                        S.peeked = null;
+                        S.token = S.input(true); // force regexp
+                }
+                switch (S.token.type) {
+                    case "num":
+                    case "string":
+                    case "regexp":
+                    case "operator":
+                    case "atom":
+                        return simple_statement();
+
+                    case "name":
+                        return is_token(peek(), "punc", ":")
+                                ? labeled_statement(prog1(S.token.value, next, next))
+                                : simple_statement();
+
+                    case "punc":
+                        switch (S.token.value) {
+                            case "{":
+                                return as("block", block_());
+                            case "[":
+                            case "(":
+                                return simple_statement();
+                            case ";":
+                                next();
+                                return as("block");
+                            default:
+                                unexpected();
+                        }
+
+                    case "keyword":
+                        switch (prog1(S.token.value, next)) {
+                            case "break":
+                                return break_cont("break");
+
+                            case "continue":
+                                return break_cont("continue");
+
+                            case "debugger":
+                                semicolon();
+                                return as("debugger");
+
+                            case "do":
+                                return (function(body){
+                                        expect_token("keyword", "while");
+                                        return as("do", prog1(parenthesised, semicolon), body);
+                                })(in_loop(statement));
+
+                            case "for":
+                                return for_();
+
+                            case "function":
+                                return function_(true);
+
+                            case "if":
+                                return if_();
+
+                            case "return":
+                                if (S.in_function == 0)
+                                        croak("'return' outside of function");
+                                return as("return",
+                                          is("punc", ";")
+                                          ? (next(), null)
+                                          : can_insert_semicolon()
+                                          ? null
+                                          : prog1(expression, semicolon));
+
+                            case "switch":
+                                return as("switch", parenthesised(), switch_block_());
+
+                            case "throw":
+                                return as("throw", prog1(expression, semicolon));
+
+                            case "try":
+                                return try_();
+
+                            case "var":
+                                return prog1(var_, semicolon);
+
+                            case "const":
+                                return prog1(const_, semicolon);
+
+                            case "while":
+                                return as("while", parenthesised(), in_loop(statement));
+
+                            case "with":
+                                return as("with", parenthesised(), statement());
+
+                            default:
+                                unexpected();
+                        }
+                }
+        };
+
+        function labeled_statement(label) {
+                S.labels.push(label);
+                var start = S.token, stat = statement();
+                if (strict_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0]))
+                        unexpected(start);
+                S.labels.pop();
+                return as("label", label, stat);
+        };
+
+        function simple_statement() {
+                return as("stat", prog1(expression, semicolon));
+        };
+
+        function break_cont(type) {
+                var name = is("name") ? S.token.value : null;
+                if (name != null) {
+                        next();
+                        if (!member(name, S.labels))
+                                croak("Label " + name + " without matching loop or statement");
+                }
+                else if (S.in_loop == 0)
+                        croak(type + " not inside a loop or switch");
+                semicolon();
+                return as(type, name);
+        };
+
+        function for_() {
+                expect("(");
+                var has_var = is("keyword", "var");
+                if (has_var)
+                        next();
+                if (is("name") && is_token(peek(), "operator", "in")) {
+                        // for (i in foo)
+                        var name = S.token.value;
+                        next(); next();
+                        var obj = expression();
+                        expect(")");
+                        return as("for-in", has_var, name, obj, in_loop(statement));
+                } else {
+                        // classic for
+                        var init = is("punc", ";") ? null : has_var ? var_() : expression();
+                        expect(";");
+                        var test = is("punc", ";") ? null : expression();
+                        expect(";");
+                        var step = is("punc", ")") ? null : expression();
+                        expect(")");
+                        return as("for", init, test, step, in_loop(statement));
+                }
+        };
+
+        function function_(in_statement) {
+                var name = is("name") ? prog1(S.token.value, next) : null;
+                if (in_statement && !name)
+                        unexpected();
+                expect("(");
+                return as(in_statement ? "defun" : "function",
+                          name,
+                          // arguments
+                          (function(first, a){
+                                  while (!is("punc", ")")) {
+                                          if (first) first = false; else expect(",");
+                                          if (!is("name")) unexpected();
+                                          a.push(S.token.value);
+                                          next();
+                                  }
+                                  next();
+                                  return a;
+                          })(true, []),
+                          // body
+                          (function(){
+                                  ++S.in_function;
+                                  var loop = S.in_loop;
+                                  S.in_loop = 0;
+                                  var a = block_();
+                                  --S.in_function;
+                                  S.in_loop = loop;
+                                  return a;
+                          })());
+        };
+
+        function if_() {
+                var cond = parenthesised(), body = statement(), belse;
+                if (is("keyword", "else")) {
+                        next();
+                        belse = statement();
+                }
+                return as("if", cond, body, belse);
+        };
+
+        function block_() {
+                expect("{");
+                var a = [];
+                while (!is("punc", "}")) {
+                        if (is("eof")) unexpected();
+                        a.push(statement());
+                }
+                next();
+                return a;
+        };
+
+        var switch_block_ = curry(in_loop, function(){
+                expect("{");
+                var a = [], cur = null;
+                while (!is("punc", "}")) {
+                        if (is("eof")) unexpected();
+                        if (is("keyword", "case")) {
+                                next();
+                                cur = [];
+                                a.push([ expression(), cur ]);
+                                expect(":");
+                        }
+                        else if (is("keyword", "default")) {
+                                next();
+                                expect(":");
+                                cur = [];
+                                a.push([ null, cur ]);
+                        }
+                        else {
+                                if (!cur) unexpected();
+                                cur.push(statement());
+                        }
+                }
+                next();
+                return a;
+        });
+
+        function try_() {
+                var body = block_(), bcatch, bfinally;
+                if (is("keyword", "catch")) {
+                        next();
+                        expect("(");
+                        if (!is("name"))
+                                croak("Name expected");
+                        var name = S.token.value;
+                        next();
+                        expect(")");
+                        bcatch = [ name, block_() ];
+                }
+                if (is("keyword", "finally")) {
+                        next();
+                        bfinally = block_();
+                }
+                if (!bcatch && !bfinally)
+                        croak("Missing catch/finally blocks");
+                return as("try", body, bcatch, bfinally);
+        };
+
+        function vardefs() {
+                var a = [];
+                for (;;) {
+                        if (!is("name"))
+                                unexpected();
+                        var name = S.token.value;
+                        next();
+                        if (is("operator", "=")) {
+                                next();
+                                a.push([ name, expression(false) ]);
+                        } else {
+                                a.push([ name ]);
+                        }
+                        if (!is("punc", ","))
+                                break;
+                        next();
+                }
+                return a;
+        };
+
+        function var_() {
+                return as("var", vardefs());
+        };
+
+        function const_() {
+                return as("const", vardefs());
+        };
+
+        function new_() {
+                var newexp = expr_atom(false), args;
+                if (is("punc", "(")) {
+                        next();
+                        args = expr_list(")");
+                } else {
+                        args = [];
+                }
+                return subscripts(as("new", newexp, args), true);
+        };
+
+        function expr_atom(allow_calls) {
+                if (is("operator", "new")) {
+                        next();
+                        return new_();
+                }
+                if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
+                        return make_unary("unary-prefix",
+                                          prog1(S.token.value, next),
+                                          expr_atom(allow_calls));
+                }
+                if (is("punc")) {
+                        switch (S.token.value) {
+                            case "(":
+                                next();
+                                return subscripts(prog1(expression, curry(expect, ")")), allow_calls);
+                            case "[":
+                                next();
+                                return subscripts(array_(), allow_calls);
+                            case "{":
+                                next();
+                                return subscripts(object_(), allow_calls);
+                        }
+                        unexpected();
+                }
+                if (is("keyword", "function")) {
+                        next();
+                        return subscripts(function_(false), allow_calls);
+                }
+                if (HOP(ATOMIC_START_TOKEN, S.token.type)) {
+                        var atom = S.token.type == "regexp"
+                                ? as("regexp", S.token.value[0], S.token.value[1])
+                                : as(S.token.type, S.token.value);
+                        return subscripts(prog1(atom, next), allow_calls);
+                }
+                unexpected();
+        };
+
+        function expr_list(closing, allow_trailing_comma, allow_empty) {
+                var first = true, a = [];
+                while (!is("punc", closing)) {
+                        if (first) first = false; else expect(",");
+                        if (allow_trailing_comma && is("punc", closing)) break;
+                        if (is("punc", ",") && allow_empty) {
+                                a.push([ "atom", "undefined" ]);
+                        } else {
+                                a.push(expression(false));
+                        }
+                }
+                next();
+                return a;
+        };
+
+        function array_() {
+                return as("array", expr_list("]", !strict_mode, true));
+        };
+
+        function object_() {
+                var first = true, a = [];
+                while (!is("punc", "}")) {
+                        if (first) first = false; else expect(",");
+                        if (!strict_mode && is("punc", "}"))
+                                // allow trailing comma
+                                break;
+                        var type = S.token.type;
+                        var name = as_property_name();
+                        if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) {
+                                a.push([ as_name(), function_(false), name ]);
+                        } else {
+                                expect(":");
+                                a.push([ name, expression(false) ]);
+                        }
+                }
+                next();
+                return as("object", a);
+        };
+
+        function as_property_name() {
+                switch (S.token.type) {
+                    case "num":
+                    case "string":
+                        return prog1(S.token.value, next);
+                }
+                return as_name();
+        };
+
+        function as_name() {
+                switch (S.token.type) {
+                    case "name":
+                    case "operator":
+                    case "keyword":
+                    case "atom":
+                        return prog1(S.token.value, next);
+                    default:
+                        unexpected();
+                }
+        };
+
+        function subscripts(expr, allow_calls) {
+                if (is("punc", ".")) {
+                        next();
+                        return subscripts(as("dot", expr, as_name()), allow_calls);
+                }
+                if (is("punc", "[")) {
+                        next();
+                        return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls);
+                }
+                if (allow_calls && is("punc", "(")) {
+                        next();
+                        return subscripts(as("call", expr, expr_list(")")), true);
+                }
+                if (allow_calls && is("operator") && HOP(UNARY_POSTFIX, S.token.value)) {
+                        return prog1(curry(make_unary, "unary-postfix", S.token.value, expr),
+                                     next);
+                }
+                return expr;
+        };
+
+        function make_unary(tag, op, expr) {
+                if ((op == "++" || op == "--") && !is_assignable(expr))
+                        croak("Invalid use of " + op + " operator");
+                return as(tag, op, expr);
+        };
+
+        function expr_op(left, min_prec) {
+                var op = is("operator") ? S.token.value : null;
+                var prec = op != null ? PRECEDENCE[op] : null;
+                if (prec != null && prec > min_prec) {
+                        next();
+                        var right = expr_op(expr_atom(true), prec);
+                        return expr_op(as("binary", op, left, right), min_prec);
+                }
+                return left;
+        };
+
+        function expr_ops() {
+                return expr_op(expr_atom(true), 0);
+        };
+
+        function maybe_conditional() {
+                var expr = expr_ops();
+                if (is("operator", "?")) {
+                        next();
+                        var yes = expression(false);
+                        expect(":");
+                        return as("conditional", expr, yes, expression(false));
+                }
+                return expr;
+        };
+
+        function is_assignable(expr) {
+                switch (expr[0]) {
+                    case "dot":
+                    case "sub":
+                        return true;
+                    case "name":
+                        return expr[1] != "this";
+                }
+        };
+
+        function maybe_assign() {
+                var left = maybe_conditional(), val = S.token.value;
+                if (is("operator") && HOP(ASSIGNMENT, val)) {
+                        if (is_assignable(left)) {
+                                next();
+                                return as("assign", ASSIGNMENT[val], left, maybe_assign());
+                        }
+                        croak("Invalid assignment");
+                }
+                return left;
+        };
+
+        function expression(commas) {
+                if (arguments.length == 0)
+                        commas = true;
+                var expr = maybe_assign();
+                if (commas && is("punc", ",")) {
+                        next();
+                        return as("seq", expr, expression());
+                }
+                return expr;
+        };
+
+        function in_loop(cont) {
+                try {
+                        ++S.in_loop;
+                        return cont();
+                } finally {
+                        --S.in_loop;
+                }
+        };
+
+        return as("toplevel", (function(a){
+                while (!is("eof"))
+                        a.push(statement());
+                return a;
+        })([]));
+
+};
+
+/* -----[ Utilities ]----- */
+
+function curry(f) {
+        var args = slice(arguments, 1);
+        return function() { return f.apply(this, args.concat(slice(arguments))); };
+};
+
+function prog1(ret) {
+        if (ret instanceof Function)
+                ret = ret();
+        for (var i = 1, n = arguments.length; --n > 0; ++i)
+                arguments[i]();
+        return ret;
+};
+
+function array_to_hash(a) {
+        var ret = {};
+        for (var i = 0; i < a.length; ++i)
+                ret[a[i]] = true;
+        return ret;
+};
+
+function slice(a, start) {
+        return Array.prototype.slice.call(a, start == null ? 0 : start);
+};
+
+function characters(str) {
+        return str.split("");
+};
+
+function member(name, array) {
+        for (var i = array.length; --i >= 0;)
+                if (array[i] === name)
+                        return true;
+        return false;
+};
+
+function HOP(obj, prop) {
+        return Object.prototype.hasOwnProperty.call(obj, prop);
+};
+
+/* -----[ Exports ]----- */
+
+exports.tokenizer = tokenizer;
+exports.parse = parse;
+exports.slice = slice;
+exports.curry = curry;
+exports.member = member;
+exports.array_to_hash = array_to_hash;
+exports.PRECEDENCE = PRECEDENCE;
+exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
+exports.RESERVED_WORDS = RESERVED_WORDS;
+exports.KEYWORDS = KEYWORDS;
+exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
+exports.OPERATORS = OPERATORS;
+exports.is_alphanumeric_char = is_alphanumeric_char;
+exports.is_identifier_char = is_identifier_char;
diff --git a/Source/devtools/front_end/UserAgentSupport.js b/Source/devtools/front_end/UserAgentSupport.js
new file mode 100644
index 0000000..4859f65
--- /dev/null
+++ b/Source/devtools/front_end/UserAgentSupport.js
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.UserAgentSupport = function()
+{
+    this._userAgentOverrideEnabled = false;
+    this._deviceMetricsOverrideEnabled = false;
+    this._geolocationPositionOverrideEnabled = false;
+    this._deviceOrientationOverrideEnabled = false;
+
+    WebInspector.settings.userAgent.addChangeListener(this._userAgentChanged, this);
+    WebInspector.settings.deviceMetrics.addChangeListener(this._deviceMetricsChanged, this);
+    WebInspector.settings.deviceFitWindow.addChangeListener(this._deviceMetricsChanged, this);
+    WebInspector.settings.geolocationOverride.addChangeListener(this._geolocationPositionChanged, this);
+    WebInspector.settings.deviceOrientationOverride.addChangeListener(this._deviceOrientationChanged, this);
+}
+
+/**
+ * @constructor
+ * @param {number} width
+ * @param {number} height
+ * @param {number} fontScaleFactor
+ */
+WebInspector.UserAgentSupport.DeviceMetrics = function(width, height, fontScaleFactor)
+{
+    this.width = width;
+    this.height = height;
+    this.fontScaleFactor = fontScaleFactor;
+}
+
+/**
+ * @return {WebInspector.UserAgentSupport.DeviceMetrics}
+ */
+WebInspector.UserAgentSupport.DeviceMetrics.parseSetting = function(value)
+{
+    if (value) {
+        var splitMetrics = value.split("x");
+        if (splitMetrics.length === 3)
+            return new WebInspector.UserAgentSupport.DeviceMetrics(parseInt(splitMetrics[0], 10), parseInt(splitMetrics[1], 10), parseFloat(splitMetrics[2]));
+    }
+    return new WebInspector.UserAgentSupport.DeviceMetrics(0, 0, 1);
+}
+
+/**
+ * @return {?WebInspector.UserAgentSupport.DeviceMetrics}
+ */
+WebInspector.UserAgentSupport.DeviceMetrics.parseUserInput = function(widthString, heightString, fontScaleFactorString)
+{
+    function isUserInputValid(value, isInteger)
+    {
+        if (!value)
+            return true;
+        return isInteger ? /^[0]*[1-9][\d]*$/.test(value) : /^[0]*([1-9][\d]*(\.\d+)?|\.\d+)$/.test(value);
+    }
+
+    if (!widthString ^ !heightString)
+        return null;
+
+    var isWidthValid = isUserInputValid(widthString, true);
+    var isHeightValid = isUserInputValid(heightString, true);
+    var isFontScaleFactorValid = isUserInputValid(fontScaleFactorString, false);
+
+    if (!isWidthValid && !isHeightValid && !isFontScaleFactorValid)
+        return null;
+
+    var width = isWidthValid ? parseInt(widthString || "0", 10) : -1;
+    var height = isHeightValid ? parseInt(heightString || "0", 10) : -1;
+    var fontScaleFactor = isFontScaleFactorValid ? parseFloat(fontScaleFactorString) : -1;
+
+    return new WebInspector.UserAgentSupport.DeviceMetrics(width, height, fontScaleFactor);
+}
+
+WebInspector.UserAgentSupport.DeviceMetrics.prototype = {
+    /**
+     * @return {boolean}
+     */
+    isValid: function()
+    {
+        return this.isWidthValid() && this.isHeightValid() && this.isFontScaleFactorValid();
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isWidthValid: function()
+    {
+        return this.width >= 0;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isHeightValid: function()
+    {
+        return this.height >= 0;
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isFontScaleFactorValid: function()
+    {
+        return this.fontScaleFactor > 0;
+    },
+
+    /**
+     * @return {string}
+     */
+    toSetting: function()
+    {
+        if (!this.isValid())
+            return "";
+
+        return this.width && this.height ? this.width + "x" + this.height + "x" + this.fontScaleFactor : "";
+    },
+
+    /**
+     * @return {string}
+     */
+    widthToInput: function()
+    {
+        return this.isWidthValid() && this.width ? String(this.width) : "";
+    },
+
+    /**
+     * @return {string}
+     */
+    heightToInput: function()
+    {
+        return this.isHeightValid() && this.height ? String(this.height) : "";
+    },
+
+    /**
+     * @return {string}
+     */
+    fontScaleFactorToInput: function()
+    {
+        return this.isFontScaleFactorValid() && this.fontScaleFactor ? String(this.fontScaleFactor) : "";
+    }
+}
+
+/**
+ * @constructor
+ * @param {number} latitude
+ * @param {number} longitude
+ */
+WebInspector.UserAgentSupport.GeolocationPosition = function(latitude, longitude, error)
+{
+    this.latitude = latitude;
+    this.longitude = longitude;
+    this.error = error;
+}
+
+WebInspector.UserAgentSupport.GeolocationPosition.prototype = {
+    /**
+     * @return {string}
+     */
+    toSetting: function()
+    {
+        return (typeof this.latitude === "number" && typeof this.longitude === "number" && typeof this.error === "string") ? this.latitude + "@" + this.longitude + ":" + this.error : "";
+    }
+}
+
+/**
+ * @return {WebInspector.UserAgentSupport.GeolocationPosition}
+ */
+WebInspector.UserAgentSupport.GeolocationPosition.parseSetting = function(value)
+{
+    if (value) {
+        var splitError = value.split(":");
+        if (splitError.length === 2) {
+            var splitPosition = splitError[0].split("@")
+            if (splitPosition.length === 2)
+                return new WebInspector.UserAgentSupport.GeolocationPosition(parseFloat(splitPosition[0]), parseFloat(splitPosition[1]), splitError[1]);
+        }
+    }
+    return new WebInspector.UserAgentSupport.GeolocationPosition(0, 0, "");
+}
+
+/**
+ * @return {?WebInspector.UserAgentSupport.GeolocationPosition}
+ */
+WebInspector.UserAgentSupport.GeolocationPosition.parseUserInput = function(latitudeString, longitudeString, errorStatus)
+{
+    function isUserInputValid(value)
+    {
+        if (!value)
+            return true;
+        return /^[-]?[0-9]*[.]?[0-9]*$/.test(value);
+    }
+
+    if (!latitudeString ^ !latitudeString)
+        return null;
+
+    var isLatitudeValid = isUserInputValid(latitudeString);
+    var isLongitudeValid = isUserInputValid(longitudeString);
+
+    if (!isLatitudeValid && !isLongitudeValid)
+        return null;
+
+    var latitude = isLatitudeValid ? parseFloat(latitudeString) : -1;
+    var longitude = isLongitudeValid ? parseFloat(longitudeString) : -1;
+
+    return new WebInspector.UserAgentSupport.GeolocationPosition(latitude, longitude, errorStatus ? "PositionUnavailable" : "");
+}
+
+WebInspector.UserAgentSupport.GeolocationPosition.clearGeolocationOverride = function()
+{
+    PageAgent.clearGeolocationOverride();
+}
+
+/**
+ * @constructor
+ * @param {number} alpha
+ * @param {number} beta
+ * @param {number} gamma
+ */
+WebInspector.UserAgentSupport.DeviceOrientation = function(alpha, beta, gamma)
+{
+    this.alpha = alpha;
+    this.beta = beta;
+    this.gamma = gamma;
+}
+
+WebInspector.UserAgentSupport.DeviceOrientation.prototype = {
+    /**
+     * @return {string}
+     */
+    toSetting: function()
+    {
+        return JSON.stringify(this);
+    }
+}
+
+/**
+ * @return {WebInspector.UserAgentSupport.DeviceOrientation}
+ */
+WebInspector.UserAgentSupport.DeviceOrientation.parseSetting = function(value)
+{
+    if (value) {
+        var jsonObject = JSON.parse(value);
+        return new WebInspector.UserAgentSupport.DeviceOrientation(jsonObject.alpha, jsonObject.beta, jsonObject.gamma);
+    }
+    return new WebInspector.UserAgentSupport.DeviceOrientation(0, 0, 0);
+}
+
+/**
+ * @return {?WebInspector.UserAgentSupport.DeviceOrientation}
+ */
+WebInspector.UserAgentSupport.DeviceOrientation.parseUserInput = function(alphaString, betaString, gammaString)
+{
+    function isUserInputValid(value)
+    {
+        if (!value)
+            return true;
+        return /^[-]?[0-9]*[.]?[0-9]*$/.test(value);
+    }
+
+    if (!alphaString ^ !betaString ^ !gammaString)
+        return null;
+
+    var isAlphaValid = isUserInputValid(alphaString);
+    var isBetaValid = isUserInputValid(betaString);
+    var isGammaValid = isUserInputValid(gammaString);
+
+    if (!isAlphaValid && !isBetaValid && !isGammaValid)
+        return null;
+
+    var alpha = isAlphaValid ? parseFloat(alphaString) : -1;
+    var beta = isBetaValid ? parseFloat(betaString) : -1;
+    var gamma = isGammaValid ? parseFloat(gammaString) : -1;
+
+    return new WebInspector.UserAgentSupport.DeviceOrientation(alpha, beta, gamma);
+}
+
+WebInspector.UserAgentSupport.DeviceOrientation.clearDeviceOrientationOverride = function()
+{
+    PageAgent.clearDeviceOrientationOverride();
+}
+
+WebInspector.UserAgentSupport.prototype = {
+    toggleUserAgentOverride: function(enabled)
+    {
+        if (enabled === this._userAgentOverrideEnabled)
+            return;
+        this._userAgentOverrideEnabled = enabled;
+        this._userAgentChanged();
+    },
+
+    toggleDeviceMetricsOverride: function(enabled)
+    {
+        if (enabled === this._deviceMetricsOverrideEnabled)
+            return;
+        this._deviceMetricsOverrideEnabled = enabled;
+        this._deviceMetricsChanged();
+    },
+
+    toggleGeolocationPositionOverride: function(enabled)
+    {
+        if (enabled === this._geolocationPositionOverrideEnabled)
+            return;
+        this._geolocationPositionOverrideEnabled = enabled;
+        this._geolocationPositionChanged();
+    },
+
+    toggleDeviceOrientationOverride: function(enabled)
+    {
+        if (enabled === this._deviceOrientationOverrideEnabled)
+            return;
+        this._deviceOrientationOverrideEnabled = enabled;
+        this._deviceOrientationChanged();
+    },
+
+    _userAgentChanged: function()
+    {
+        NetworkAgent.setUserAgentOverride(this._userAgentOverrideEnabled ? WebInspector.settings.userAgent.get() : "");
+    },
+
+    _deviceMetricsChanged: function()
+    {
+        var metrics = WebInspector.UserAgentSupport.DeviceMetrics.parseSetting(this._deviceMetricsOverrideEnabled ? WebInspector.settings.deviceMetrics.get() : "");
+        if (metrics.isValid())
+            PageAgent.setDeviceMetricsOverride(metrics.width, metrics.height, metrics.fontScaleFactor, WebInspector.settings.deviceFitWindow.get());
+    },
+
+    _geolocationPositionChanged: function()
+    {
+        if (!this._geolocationPositionOverrideEnabled) {
+            PageAgent.clearGeolocationOverride();
+            return;
+        }
+        var geolocation = WebInspector.UserAgentSupport.GeolocationPosition.parseSetting(WebInspector.settings.geolocationOverride.get());
+        if (geolocation.error)
+            PageAgent.setGeolocationOverride();
+        else
+            PageAgent.setGeolocationOverride(geolocation.latitude, geolocation.longitude, 150);
+    },
+
+    _deviceOrientationChanged: function()
+    {
+        if (!this._deviceOrientationOverrideEnabled) {
+            PageAgent.clearDeviceOrientationOverride();
+            return;
+        }
+        var deviceOrientation = WebInspector.UserAgentSupport.DeviceOrientation.parseSetting(WebInspector.settings.deviceOrientationOverride.get());
+        PageAgent.setDeviceOrientationOverride(deviceOrientation.alpha, deviceOrientation.beta, deviceOrientation.gamma);
+    }
+}
+
+
+/**
+ * @type {WebInspector.UserAgentSupport} 
+ */
+WebInspector.userAgentSupport;
diff --git a/Source/devtools/front_end/UserMetrics.js b/Source/devtools/front_end/UserMetrics.js
new file mode 100644
index 0000000..0c436ad
--- /dev/null
+++ b/Source/devtools/front_end/UserMetrics.js
@@ -0,0 +1,130 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.UserMetrics = function()
+{
+    for (var actionName in WebInspector.UserMetrics._ActionCodes) {
+        var actionCode = WebInspector.UserMetrics._ActionCodes[actionName];
+        this[actionName] = new WebInspector.UserMetrics._Recorder(actionCode);
+    }
+
+    function settingChanged(trueCode, falseCode, event)
+    {
+        if (event.data)
+            InspectorFrontendHost.recordSettingChanged(trueCode);
+        else
+            InspectorFrontendHost.recordSettingChanged(falseCode);
+    }
+
+    WebInspector.settings.domWordWrap.addChangeListener(settingChanged.bind(this, WebInspector.UserMetrics._SettingCodes.ElementsDOMWrapOn, WebInspector.UserMetrics._SettingCodes.ElementsDOMWrapOff));
+    WebInspector.settings.monitoringXHREnabled.addChangeListener(settingChanged.bind(this, WebInspector.UserMetrics._SettingCodes.ConsoleMonitorXHROn, WebInspector.UserMetrics._SettingCodes.ConsoleMonitorXHROff));
+    WebInspector.settings.preserveConsoleLog.addChangeListener(settingChanged.bind(this, WebInspector.UserMetrics._SettingCodes.ConsolePreserveLogOn, WebInspector.UserMetrics._SettingCodes.ConsolePreserveLogOff));
+    WebInspector.settings.resourcesLargeRows.addChangeListener(settingChanged.bind(this, WebInspector.UserMetrics._SettingCodes.NetworkShowLargeRowsOn, WebInspector.UserMetrics._SettingCodes.NetworkShowLargeRowsOff));
+}
+
+// Codes below are used to collect UMA histograms in the Chromium port.
+// Do not change the values below, additional actions are needed on the Chromium side
+// in order to add more codes.
+
+WebInspector.UserMetrics._ActionCodes = {
+    WindowDocked: 1,
+    WindowUndocked: 2,
+    ScriptsBreakpointSet: 3,
+    TimelineStarted: 4,
+    ProfilesCPUProfileTaken: 5,
+    ProfilesHeapProfileTaken: 6,
+    AuditsStarted: 7,
+    ConsoleEvaluated: 8
+}
+
+WebInspector.UserMetrics._SettingCodes = {
+    ElementsDOMWrapOn: 1,
+    ElementsDOMWrapOff: 2,
+    ConsoleMonitorXHROn: 3,
+    ConsoleMonitorXHROff: 4,
+    ConsolePreserveLogOn: 5,
+    ConsolePreserveLogOff: 6,
+    NetworkShowLargeRowsOn: 7,
+    NetworkShowLargeRowsOff: 8
+}
+
+WebInspector.UserMetrics._PanelCodes = {
+    elements: 1,
+    resources: 2,
+    network: 3,
+    scripts: 4,
+    timeline: 5,
+    profiles: 6,
+    audits: 7,
+    console: 8
+}
+
+WebInspector.UserMetrics.UserAction = "UserAction";
+
+WebInspector.UserMetrics.UserActionNames = {
+    ForcedElementState: "forcedElementState",
+    FileSaved: "fileSaved",
+    RevertRevision: "revertRevision",
+    ApplyOriginalContent: "applyOriginalContent",
+    TogglePrettyPrint: "togglePrettyPrint",
+    SetBreakpoint: "setBreakpoint",
+    OpenSourceLink: "openSourceLink",
+    NetworkSort: "networkSort",
+    NetworkRequestSelected: "networkRequestSelected",
+    NetworkRequestTabSelected: "networkRequestTabSelected",
+    HeapSnapshotFilterChanged: "heapSnapshotFilterChanged"
+};
+
+WebInspector.UserMetrics.prototype = {
+    panelShown: function(panelName)
+    {
+        InspectorFrontendHost.recordPanelShown(WebInspector.UserMetrics._PanelCodes[panelName] || 0);
+    }
+}
+
+/**
+ * @constructor
+ */
+WebInspector.UserMetrics._Recorder = function(actionCode)
+{
+    this._actionCode = actionCode;
+}
+
+WebInspector.UserMetrics._Recorder.prototype = {
+    record: function()
+    {
+        InspectorFrontendHost.recordActionTaken(this._actionCode);
+    }
+}
+
+WebInspector.userMetrics = new WebInspector.UserMetrics();
diff --git a/Source/devtools/front_end/View.js b/Source/devtools/front_end/View.js
new file mode 100644
index 0000000..76784d2
--- /dev/null
+++ b/Source/devtools/front_end/View.js
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2008 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. ``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
+ * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.View = function()
+{
+    this.element = document.createElement("div");
+    this.element.__view = this;
+    this._visible = true;
+    this._isRoot = false;
+    this._isShowing = false;
+    this._children = [];
+    this._hideOnDetach = false;
+    this._cssFiles = [];
+    this._notificationDepth = 0;
+}
+
+WebInspector.View._cssFileToVisibleViewCount = {};
+WebInspector.View._cssFileToStyleElement = {};
+WebInspector.View._cssUnloadTimeout = 2000;
+
+WebInspector.View.prototype = {
+    /**
+     * @return {?Element}
+     */
+    statusBarText: function()
+    {
+        return null;
+    },
+
+    markAsRoot: function()
+    {
+        WebInspector.View._assert(!this.element.parentElement, "Attempt to mark as root attached node");
+        this._isRoot = true;
+    },
+
+    /**
+     * @return {?WebInspector.View}
+     */
+    parentView: function()
+    {
+        return this._parentView;
+    },
+
+    isShowing: function()
+    {
+        return this._isShowing;
+    },
+
+    setHideOnDetach: function()
+    {
+        this._hideOnDetach = true;
+    },
+
+    /**
+     * @return {boolean} 
+     */
+    _inNotification: function()
+    {
+        return !!this._notificationDepth || (this._parentView && this._parentView._inNotification());
+    },
+
+    _parentIsShowing: function()
+    {
+        if (this._isRoot)
+            return true;
+        return this._parentView && this._parentView.isShowing();
+    },
+
+    /**
+     * @param {function(this:WebInspector.View)} method
+     */
+    _callOnVisibleChildren: function(method)
+    {
+        var copy = this._children.slice();
+        for (var i = 0; i < copy.length; ++i) {
+            if (copy[i]._parentView === this && copy[i]._visible)
+                method.call(copy[i]);
+        }
+    },
+
+    _processWillShow: function()
+    {
+        this._loadCSSIfNeeded();
+        this._callOnVisibleChildren(this._processWillShow);
+    },
+
+    _processWasShown: function()
+    {
+        if (this._inNotification())
+            return;
+        this._isShowing = true;
+        this.restoreScrollPositions();
+        this._notify(this.wasShown);
+        this._notify(this.onResize);
+        this._callOnVisibleChildren(this._processWasShown);
+    },
+
+    _processWillHide: function()
+    {
+        if (this._inNotification())
+            return;
+        this.storeScrollPositions();
+
+        this._callOnVisibleChildren(this._processWillHide);
+        this._notify(this.willHide);
+        this._isShowing = false;
+    },
+
+    _processWasHidden: function()
+    {
+        this._disableCSSIfNeeded();
+        this._callOnVisibleChildren(this._processWasHidden);
+    },
+
+    _processOnResize: function()
+    {
+        if (this._inNotification())
+            return;
+        if (!this.isShowing())
+            return;
+        this._notify(this.onResize);
+        this._callOnVisibleChildren(this._processOnResize);
+    },
+
+    /**
+     * @param {function(this:WebInspector.View)} notification
+     */
+    _notify: function(notification)
+    {
+        ++this._notificationDepth;
+        try {
+            notification.call(this);
+        } finally {
+            --this._notificationDepth;
+        }
+    },
+
+    wasShown: function()
+    {
+    },
+
+    willHide: function()
+    {
+    },
+
+    onResize: function()
+    {
+    },
+
+    /**
+     * @param {Element} parentElement
+     * @param {Element=} insertBefore
+     */
+    show: function(parentElement, insertBefore)
+    {
+        WebInspector.View._assert(parentElement, "Attempt to attach view with no parent element");
+
+        // Update view hierarchy
+        if (this.element.parentElement !== parentElement) {
+            if (this.element.parentElement)
+                this.detach();
+
+            var currentParent = parentElement;
+            while (currentParent && !currentParent.__view)
+                currentParent = currentParent.parentElement;
+
+            if (currentParent) {
+                this._parentView = currentParent.__view;
+                this._parentView._children.push(this);
+                this._isRoot = false;
+            } else
+                WebInspector.View._assert(this._isRoot, "Attempt to attach view to orphan node");
+        } else if (this._visible)
+            return;
+
+        this._visible = true;
+
+        if (this._parentIsShowing())
+            this._processWillShow();
+
+        this.element.addStyleClass("visible");
+
+        // Reparent
+        if (this.element.parentElement !== parentElement) {
+            WebInspector.View._incrementViewCounter(parentElement, this.element);
+            if (insertBefore)
+                WebInspector.View._originalInsertBefore.call(parentElement, this.element, insertBefore);
+            else
+                WebInspector.View._originalAppendChild.call(parentElement, this.element);
+        }
+
+        if (this._parentIsShowing())
+            this._processWasShown();
+    },
+
+    /**
+     * @param {boolean=} overrideHideOnDetach
+     */
+    detach: function(overrideHideOnDetach)
+    {
+        var parentElement = this.element.parentElement;
+        if (!parentElement)
+            return;
+
+        if (this._parentIsShowing())
+            this._processWillHide();
+
+        if (this._hideOnDetach && !overrideHideOnDetach) {
+            this.element.removeStyleClass("visible");
+            this._visible = false;
+            if (this._parentIsShowing())
+                this._processWasHidden();
+            return;
+        }
+
+        // Force legal removal
+        WebInspector.View._decrementViewCounter(parentElement, this.element);
+        WebInspector.View._originalRemoveChild.call(parentElement, this.element);
+
+        this._visible = false;
+        if (this._parentIsShowing())
+            this._processWasHidden();
+
+        // Update view hierarchy
+        if (this._parentView) {
+            var childIndex = this._parentView._children.indexOf(this);
+            WebInspector.View._assert(childIndex >= 0, "Attempt to remove non-child view");
+            this._parentView._children.splice(childIndex, 1);
+            this._parentView = null;
+        } else
+            WebInspector.View._assert(this._isRoot, "Removing non-root view from DOM");
+    },
+
+    detachChildViews: function()
+    {
+        var children = this._children.slice();
+        for (var i = 0; i < children.length; ++i)
+            children[i].detach();
+    },
+
+    elementsToRestoreScrollPositionsFor: function()
+    {
+        return [this.element];
+    },
+
+    storeScrollPositions: function()
+    {
+        var elements = this.elementsToRestoreScrollPositionsFor();
+        for (var i = 0; i < elements.length; ++i) {
+            var container = elements[i];
+            container._scrollTop = container.scrollTop;
+            container._scrollLeft = container.scrollLeft;
+        }
+    },
+
+    restoreScrollPositions: function()
+    {
+        var elements = this.elementsToRestoreScrollPositionsFor();
+        for (var i = 0; i < elements.length; ++i) {
+            var container = elements[i];
+            if (container._scrollTop)
+                container.scrollTop = container._scrollTop;
+            if (container._scrollLeft)
+                container.scrollLeft = container._scrollLeft;
+        }
+    },
+
+    canHighlightLine: function()
+    {
+        return false;
+    },
+
+    highlightLine: function(line)
+    {
+    },
+
+    doResize: function()
+    {
+        this._processOnResize();
+    },
+
+    registerRequiredCSS: function(cssFile)
+    {
+        if (window.flattenImports)
+            cssFile = cssFile.split("/").reverse()[0];
+        this._cssFiles.push(cssFile);
+    },
+
+    _loadCSSIfNeeded: function()
+    {
+        for (var i = 0; i < this._cssFiles.length; ++i) {
+            var cssFile = this._cssFiles[i];
+
+            var viewsWithCSSFile = WebInspector.View._cssFileToVisibleViewCount[cssFile];
+            WebInspector.View._cssFileToVisibleViewCount[cssFile] = (viewsWithCSSFile || 0) + 1;
+            if (!viewsWithCSSFile)
+                this._doLoadCSS(cssFile);
+        }
+    },
+
+    _doLoadCSS: function(cssFile)
+    {
+        var styleElement = WebInspector.View._cssFileToStyleElement[cssFile];
+        if (styleElement) {
+            styleElement.disabled = false;
+            return;
+        }
+
+        if (window.debugCSS) { /* debugging support */
+            styleElement = document.createElement("link");
+            styleElement.rel = "stylesheet";
+            styleElement.type = "text/css";
+            styleElement.href = cssFile;
+        } else {
+            var xhr = new XMLHttpRequest();
+            xhr.open("GET", cssFile, false);
+            xhr.send(null);
+
+            styleElement = document.createElement("style");
+            styleElement.type = "text/css";
+            styleElement.textContent = xhr.responseText;
+        }
+        document.head.insertBefore(styleElement, document.head.firstChild);
+
+        WebInspector.View._cssFileToStyleElement[cssFile] = styleElement;
+    },
+
+    _disableCSSIfNeeded: function()
+    {
+        var scheduleUnload = !!WebInspector.View._cssUnloadTimer;
+
+        for (var i = 0; i < this._cssFiles.length; ++i) {
+            var cssFile = this._cssFiles[i];
+
+            if (!--WebInspector.View._cssFileToVisibleViewCount[cssFile])
+                scheduleUnload = true;
+        }
+
+        function doUnloadCSS()
+        {
+            delete WebInspector.View._cssUnloadTimer;
+
+            for (cssFile in WebInspector.View._cssFileToVisibleViewCount) {
+                if (WebInspector.View._cssFileToVisibleViewCount.hasOwnProperty(cssFile)
+                    && !WebInspector.View._cssFileToVisibleViewCount[cssFile])
+                    WebInspector.View._cssFileToStyleElement[cssFile].disabled = true;
+            }
+        }
+
+        if (scheduleUnload) {
+            if (WebInspector.View._cssUnloadTimer)
+                clearTimeout(WebInspector.View._cssUnloadTimer);
+
+            WebInspector.View._cssUnloadTimer = setTimeout(doUnloadCSS, WebInspector.View._cssUnloadTimeout)
+        }
+    },
+
+    printViewHierarchy: function()
+    {
+        var lines = [];
+        this._collectViewHierarchy("", lines);
+        console.log(lines.join("\n"));
+    },
+
+    _collectViewHierarchy: function(prefix, lines)
+    {
+        lines.push(prefix + "[" + this.element.className + "]" + (this._children.length ? " {" : ""));
+
+        for (var i = 0; i < this._children.length; ++i)
+            this._children[i]._collectViewHierarchy(prefix + "    ", lines);
+
+        if (this._children.length)
+            lines.push(prefix + "}");
+    },
+
+    /**
+     * @return {Element}
+     */
+    defaultFocusedElement: function()
+    {
+        return this._defaultFocusedElement || this.element;
+    },
+
+    /**
+     * @param {Element} element
+     */
+    setDefaultFocusedElement: function(element)
+    {
+        this._defaultFocusedElement = element;
+    },
+
+    focus: function()
+    {
+        var element = this.defaultFocusedElement();
+        if (!element || element.isAncestor(document.activeElement))
+            return;
+
+        WebInspector.setCurrentFocusElement(element);
+    },
+
+    /**
+     * @return {Size}
+     */
+    measurePreferredSize: function()
+    {
+        this._loadCSSIfNeeded();
+        WebInspector.View._originalAppendChild.call(document.body, this.element);
+        this.element.positionAt(0, 0);
+        var result = new Size(this.element.offsetWidth, this.element.offsetHeight);
+        this.element.positionAt(undefined, undefined);
+        WebInspector.View._originalRemoveChild.call(document.body, this.element);
+        this._disableCSSIfNeeded();
+        return result;
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+WebInspector.View._originalAppendChild = Element.prototype.appendChild;
+WebInspector.View._originalInsertBefore = Element.prototype.insertBefore;
+WebInspector.View._originalRemoveChild = Element.prototype.removeChild;
+WebInspector.View._originalRemoveChildren = Element.prototype.removeChildren;
+
+WebInspector.View._incrementViewCounter = function(parentElement, childElement)
+{
+    var count = (childElement.__viewCounter || 0) + (childElement.__view ? 1 : 0);
+    if (!count)
+        return;
+
+    while (parentElement) {
+        parentElement.__viewCounter = (parentElement.__viewCounter || 0) + count;
+        parentElement = parentElement.parentElement;
+    }
+}
+
+WebInspector.View._decrementViewCounter = function(parentElement, childElement)
+{
+    var count = (childElement.__viewCounter || 0) + (childElement.__view ? 1 : 0);
+    if (!count)
+        return;
+
+    while (parentElement) {
+        parentElement.__viewCounter -= count;
+        parentElement = parentElement.parentElement;
+    }
+}
+
+WebInspector.View._assert = function(condition, message)
+{
+    if (!condition) {
+        console.trace();
+        throw new Error(message);
+    }
+}
+
+Element.prototype.appendChild = function(child)
+{
+    WebInspector.View._assert(!child.__view, "Attempt to add view via regular DOM operation.");
+    return WebInspector.View._originalAppendChild.call(this, child);
+}
+
+Element.prototype.insertBefore = function(child, anchor)
+{
+    WebInspector.View._assert(!child.__view, "Attempt to add view via regular DOM operation.");
+    return WebInspector.View._originalInsertBefore.call(this, child, anchor);
+}
+
+
+Element.prototype.removeChild = function(child)
+{
+    WebInspector.View._assert(!child.__viewCounter && !child.__view, "Attempt to remove element containing view via regular DOM operation");
+    return WebInspector.View._originalRemoveChild.call(this, child);
+}
+
+Element.prototype.removeChildren = function()
+{
+    WebInspector.View._assert(!this.__viewCounter, "Attempt to remove element containing view via regular DOM operation");
+    WebInspector.View._originalRemoveChildren.call(this);
+}
diff --git a/Source/devtools/front_end/ViewportControl.js b/Source/devtools/front_end/ViewportControl.js
new file mode 100644
index 0000000..5154efc
--- /dev/null
+++ b/Source/devtools/front_end/ViewportControl.js
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @param {WebInspector.ViewportControl.Provider} provider
+ */
+WebInspector.ViewportControl = function(provider)
+{
+    this.element = document.createElement("div");
+    this.element.className = "fill";
+    this.element.style.overflow = "auto";
+    this._topGapElement = this.element.createChild("div");
+    this._contentElement = this.element.createChild("div");
+    this._bottomGapElement = this.element.createChild("div");
+
+    this._provider = provider;
+    this.element.addEventListener("scroll", this._onScroll.bind(this), false);
+    this._firstVisibleIndex = 0;
+    this._lastVisibleIndex = -1;
+}
+
+/**
+ * @interface
+ */
+WebInspector.ViewportControl.Provider = function() 
+{
+}
+
+WebInspector.ViewportControl.Provider.prototype = { 
+    /**
+     * @return {number}
+     */
+    itemCount: function() { return 0; },
+
+    /**
+     * @param {number} index
+     * @return {Element}
+     */
+    itemElement: function(index) { return null; }
+}
+
+WebInspector.ViewportControl.prototype = {
+    /**
+     * @return {Element}
+     */
+    contentElement: function()
+    {
+        return this._contentElement;
+    },
+
+    refresh: function()
+    {
+        if (!this.element.clientHeight)
+            return;  // Do nothing for invisible controls.
+
+        this._contentElement.removeChildren();
+        var itemCount = this._provider.itemCount();
+        if (!itemCount) {
+            this._firstVisibleIndex = -1;
+            this._lastVisibleIndex = -1;
+            return;
+        }
+
+        if (!this._rowHeight) {
+            var firstElement = this._provider.itemElement(0);
+            this._rowHeight = firstElement.measurePreferredSize(this._contentElement).height;
+        }
+
+        var visibleFrom = this.element.scrollTop;
+        var visibleTo = visibleFrom + this.element.clientHeight;
+
+        this._firstVisibleIndex = Math.floor(visibleFrom / this._rowHeight);
+        this._lastVisibleIndex = Math.min(Math.ceil(visibleTo / this._rowHeight), itemCount) - 1;
+
+        this._topGapElement.style.height = (this._rowHeight * this._firstVisibleIndex) + "px";
+        this._bottomGapElement.style.height = (this._rowHeight * (itemCount - this._lastVisibleIndex - 1)) + "px"; 
+
+        for (var i = this._firstVisibleIndex; i <= this._lastVisibleIndex; ++i)
+            this._contentElement.appendChild(this._provider.itemElement(i));
+    },
+
+    /**
+     * @param {Event} event
+     */
+    _onScroll: function(event)
+    {
+        this.refresh();
+    },
+
+    /**
+     * @return {number}
+     */
+    rowsPerViewport: function()
+    {
+        return Math.floor(this.element.clientHeight / this._rowHeight);
+    },
+
+    /**
+     * @return {number}
+     */
+    firstVisibleIndex: function()
+    {
+        return this._firstVisibleIndex;
+    },
+
+    /**
+     * @return {number}
+     */
+    lastVisibleIndex: function()
+    {
+        return this._lastVisibleIndex;
+    },
+
+    /**
+     * @return {?Element}
+     */
+    renderedElementAt: function(index)
+    {
+        if (index < this._firstVisibleIndex)
+            return null;
+        if (index > this._lastVisibleIndex)
+            return null;
+        return this._contentElement.childNodes[index - this._firstVisibleIndex];
+    },
+
+    /**
+     * @param {number} index
+     * @param {boolean=} makeLast
+     */
+    scrollItemIntoView: function(index, makeLast)
+    {
+        if (index > this._firstVisibleIndex && index < this._lastVisibleIndex)
+            return;
+
+        if (makeLast)
+            this.element.scrollTop = this._rowHeight * (index + 1) - this.element.clientHeight;
+        else
+            this.element.scrollTop = this._rowHeight * index;
+    }
+}
diff --git a/Source/devtools/front_end/WatchExpressionsSidebarPane.js b/Source/devtools/front_end/WatchExpressionsSidebarPane.js
new file mode 100644
index 0000000..f196163
--- /dev/null
+++ b/Source/devtools/front_end/WatchExpressionsSidebarPane.js
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) IBM Corp. 2009  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 IBM Corp. 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.WatchExpressionsSidebarPane = function()
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Watch Expressions"));
+
+    this.section = new WebInspector.WatchExpressionsSection();
+    this.bodyElement.appendChild(this.section.element);
+
+    var refreshButton = document.createElement("button");
+    refreshButton.className = "pane-title-button refresh";
+    refreshButton.addEventListener("click", this._refreshButtonClicked.bind(this), false);
+    refreshButton.title = WebInspector.UIString("Refresh");
+    this.titleElement.appendChild(refreshButton);
+
+    var addButton = document.createElement("button");
+    addButton.className = "pane-title-button add";
+    addButton.addEventListener("click", this._addButtonClicked.bind(this), false);
+    this.titleElement.appendChild(addButton);
+    addButton.title = WebInspector.UIString("Add watch expression");
+
+    this._requiresUpdate = true;
+}
+
+WebInspector.WatchExpressionsSidebarPane.prototype = {
+    wasShown: function()
+    {
+        this._refreshExpressionsIfNeeded();
+    },
+
+    reset: function()
+    {
+        this.refreshExpressions();
+    },
+
+    refreshExpressions: function()
+    {
+        this._requiresUpdate = true;
+        this._refreshExpressionsIfNeeded();
+    },
+
+    addExpression: function(expression)
+    {
+        this.section.addExpression(expression);
+        this.expand();
+    },
+
+    _refreshExpressionsIfNeeded: function()
+    {
+        if (this._requiresUpdate && this.isShowing()) {
+            this.section.update();
+            delete this._requiresUpdate;
+        } else
+            this._requiresUpdate = true;
+    },
+
+    _addButtonClicked: function(event)
+    {
+        event.consume();
+        this.expand();
+        this.section.addNewExpressionAndEdit();
+    },
+
+    _refreshButtonClicked: function(event)
+    {
+        event.consume();
+        this.refreshExpressions();
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ObjectPropertiesSection}
+ */
+WebInspector.WatchExpressionsSection = function()
+{
+    this._watchObjectGroupId = "watch-group";
+
+    WebInspector.ObjectPropertiesSection.call(this, WebInspector.RemoteObject.fromPrimitiveValue(""));
+
+    this.treeElementConstructor = WebInspector.WatchedPropertyTreeElement;
+    this._expandedExpressions = {};
+    this._expandedProperties = {};
+
+    this.emptyElement = document.createElement("div");
+    this.emptyElement.className = "info";
+    this.emptyElement.textContent = WebInspector.UIString("No Watch Expressions");
+
+    this.watchExpressions = WebInspector.settings.watchExpressions.get();
+
+    this.headerElement.className = "hidden";
+    this.editable = true;
+    this.expanded = true;
+    this.propertiesElement.addStyleClass("watch-expressions");
+
+    this.element.addEventListener("mousemove", this._mouseMove.bind(this), true);
+    this.element.addEventListener("mouseout", this._mouseOut.bind(this), true);
+    this.element.addEventListener("dblclick", this._sectionDoubleClick.bind(this), false);
+    this.emptyElement.addEventListener("contextmenu", this._emptyElementContextMenu.bind(this), false);
+}
+
+WebInspector.WatchExpressionsSection.NewWatchExpression = "\xA0";
+
+WebInspector.WatchExpressionsSection.prototype = {
+    update: function(e)
+    {
+        if (e)
+            e.consume();
+
+        function appendResult(expression, watchIndex, result, wasThrown)
+        {
+            if (!result)
+                return;
+
+            var property = new WebInspector.RemoteObjectProperty(expression, result);
+            property.watchIndex = watchIndex;
+            property.wasThrown = wasThrown;
+
+            // To clarify what's going on here:
+            // In the outer function, we calculate the number of properties
+            // that we're going to be updating, and set that in the
+            // propertyCount variable.
+            // In this function, we test to see when we are processing the
+            // last property, and then call the superclass's updateProperties()
+            // method to get all the properties refreshed at once.
+            properties.push(property);
+
+            if (properties.length == propertyCount) {
+                this.updateProperties(properties, [], WebInspector.WatchExpressionTreeElement, WebInspector.WatchExpressionsSection.CompareProperties);
+
+                // check to see if we just added a new watch expression,
+                // which will always be the last property
+                if (this._newExpressionAdded) {
+                    delete this._newExpressionAdded;
+
+                    var treeElement = this.findAddedTreeElement();
+                    if (treeElement)
+                        treeElement.startEditing();
+                }
+
+                // Force displaying delete button for hovered element.
+                if (this._lastMouseMovePageY)
+                    this._updateHoveredElement(this._lastMouseMovePageY);
+            }
+        }
+
+        // TODO: pass exact injected script id.
+        RuntimeAgent.releaseObjectGroup(this._watchObjectGroupId)
+        var properties = [];
+
+        // Count the properties, so we known when to call this.updateProperties()
+        // in appendResult()
+        var propertyCount = 0;
+        for (var i = 0; i < this.watchExpressions.length; ++i) {
+            if (!this.watchExpressions[i])
+                continue;
+            ++propertyCount;
+        }
+
+        // Now process all the expressions, since we have the actual count,
+        // which is checked in the appendResult inner function.
+        for (var i = 0; i < this.watchExpressions.length; ++i) {
+            var expression = this.watchExpressions[i];
+            if (!expression)
+                continue;
+
+            WebInspector.runtimeModel.evaluate(expression, this._watchObjectGroupId, false, true, false, false, appendResult.bind(this, expression, i));
+        }
+
+        if (!propertyCount) {
+            if (!this.emptyElement.parentNode)
+                this.element.appendChild(this.emptyElement);
+        } else {
+            if (this.emptyElement.parentNode)
+                this.element.removeChild(this.emptyElement);
+        }
+
+        // note this is setting the expansion of the tree, not the section;
+        // with no expressions, and expanded tree, we get some extra vertical
+        // white space
+        this.expanded = (propertyCount != 0);
+    },
+
+    addExpression: function(expression)
+    {
+        this.watchExpressions.push(expression);
+        this.saveExpressions();
+        this.update();
+    },
+
+    addNewExpressionAndEdit: function()
+    {
+        this._newExpressionAdded = true;
+        this.watchExpressions.push(WebInspector.WatchExpressionsSection.NewWatchExpression);
+        this.update();
+    },
+
+    _sectionDoubleClick: function(event)
+    {
+        if (event.target !== this.element && event.target !== this.propertiesElement && event.target !== this.emptyElement)
+            return;
+        event.consume();
+        this.addNewExpressionAndEdit();
+    },
+
+    updateExpression: function(element, value)
+    {
+        if (value === null) {
+            var index = element.property.watchIndex;
+            this.watchExpressions.splice(index, 1);
+        }
+        else
+            this.watchExpressions[element.property.watchIndex] = value;
+        this.saveExpressions();
+        this.update();
+    },
+    
+    _deleteAllExpressions: function()
+    {
+        this.watchExpressions = [];
+        this.saveExpressions();
+        this.update();
+    },
+
+    findAddedTreeElement: function()
+    {
+        var children = this.propertiesTreeOutline.children;
+        for (var i = 0; i < children.length; ++i) {
+            if (children[i].property.name === WebInspector.WatchExpressionsSection.NewWatchExpression)
+                return children[i];
+        }
+    },
+
+    saveExpressions: function()
+    {
+        var toSave = [];
+        for (var i = 0; i < this.watchExpressions.length; i++)
+            if (this.watchExpressions[i])
+                toSave.push(this.watchExpressions[i]);
+
+        WebInspector.settings.watchExpressions.set(toSave);
+        return toSave.length;
+    },
+
+    _mouseMove: function(e)
+    {
+        if (this.propertiesElement.firstChild)
+            this._updateHoveredElement(e.pageY);
+    },
+
+    _mouseOut: function()
+    {
+        if (this._hoveredElement) {
+            this._hoveredElement.removeStyleClass("hovered");
+            delete this._hoveredElement;
+        }
+        delete this._lastMouseMovePageY;
+    },
+
+    _updateHoveredElement: function(pageY)
+    {
+        var candidateElement = this.propertiesElement.firstChild;
+        while (true) {
+            var next = candidateElement.nextSibling;
+            while (next && !next.clientHeight)
+                next = next.nextSibling;
+            if (!next || next.totalOffsetTop() > pageY)
+                break;
+            candidateElement = next;
+        }
+
+        if (this._hoveredElement !== candidateElement) {
+            if (this._hoveredElement)
+                this._hoveredElement.removeStyleClass("hovered");
+            if (candidateElement)
+                candidateElement.addStyleClass("hovered");
+            this._hoveredElement = candidateElement;
+        }
+
+        this._lastMouseMovePageY = pageY;
+    },
+
+    _emptyElementContextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add watch expression" : "Add Watch Expression"), this.addNewExpressionAndEdit.bind(this));
+        contextMenu.show();
+    },
+
+    __proto__: WebInspector.ObjectPropertiesSection.prototype
+}
+
+WebInspector.WatchExpressionsSection.CompareProperties = function(propertyA, propertyB)
+{
+    if (propertyA.watchIndex == propertyB.watchIndex)
+        return 0;
+    else if (propertyA.watchIndex < propertyB.watchIndex)
+        return -1;
+    else
+        return 1;
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.ObjectPropertyTreeElement}
+ * @param {WebInspector.RemoteObjectProperty} property
+ */
+WebInspector.WatchExpressionTreeElement = function(property)
+{
+    WebInspector.ObjectPropertyTreeElement.call(this, property);
+}
+
+WebInspector.WatchExpressionTreeElement.prototype = {
+    onexpand: function()
+    {
+        WebInspector.ObjectPropertyTreeElement.prototype.onexpand.call(this);
+        this.treeOutline.section._expandedExpressions[this._expression()] = true;
+    },
+
+    oncollapse: function()
+    {
+        WebInspector.ObjectPropertyTreeElement.prototype.oncollapse.call(this);
+        delete this.treeOutline.section._expandedExpressions[this._expression()];
+    },
+
+    onattach: function()
+    {
+        WebInspector.ObjectPropertyTreeElement.prototype.onattach.call(this);
+        if (this.treeOutline.section._expandedExpressions[this._expression()])
+            this.expanded = true;
+    },
+
+    _expression: function()
+    {
+        return this.property.name;
+    },
+
+    update: function()
+    {
+        WebInspector.ObjectPropertyTreeElement.prototype.update.call(this);
+
+        if (this.property.wasThrown) {
+            this.valueElement.textContent = WebInspector.UIString("<not available>");
+            this.listItemElement.addStyleClass("dimmed");
+        } else
+            this.listItemElement.removeStyleClass("dimmed");
+
+        var deleteButton = document.createElement("input");
+        deleteButton.type = "button";
+        deleteButton.title = WebInspector.UIString("Delete watch expression.");
+        deleteButton.addStyleClass("enabled-button");
+        deleteButton.addStyleClass("delete-button");
+        deleteButton.addEventListener("click", this._deleteButtonClicked.bind(this), false);
+        this.listItemElement.addEventListener("contextmenu", this._contextMenu.bind(this), false);
+        this.listItemElement.insertBefore(deleteButton, this.listItemElement.firstChild);
+    },
+
+    /**
+     * @param {WebInspector.ContextMenu} contextMenu
+     * @override
+     */
+    populateContextMenu: function(contextMenu)
+    {
+        if (!this.isEditing()) {
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add watch expression" : "Add Watch Expression"), this.treeOutline.section.addNewExpressionAndEdit.bind(this.treeOutline.section));
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Delete watch expression" : "Delete Watch Expression"), this._deleteButtonClicked.bind(this));
+        }
+        if (this.treeOutline.section.watchExpressions.length > 1)
+            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Delete all watch expressions" : "Delete All Watch Expressions"), this._deleteAllButtonClicked.bind(this));
+    },
+
+    _contextMenu: function(event)
+    {
+        var contextMenu = new WebInspector.ContextMenu(event);
+        this.populateContextMenu(contextMenu);
+        contextMenu.show();
+    },
+
+    _deleteAllButtonClicked: function()
+    {
+        this.treeOutline.section._deleteAllExpressions();
+    },
+
+    _deleteButtonClicked: function()
+    {
+        this.treeOutline.section.updateExpression(this, null);
+    },
+
+    renderPromptAsBlock: function()
+    {
+        return true;
+    },
+
+    /**
+     * @param {Event=} event
+     */
+    elementAndValueToEdit: function(event)
+    {
+        return [this.nameElement, this.property.name.trim()];
+    },
+
+    editingCancelled: function(element, context)
+    {
+        if (!context.elementToEdit.textContent)
+            this.treeOutline.section.updateExpression(this, null);
+
+        WebInspector.ObjectPropertyTreeElement.prototype.editingCancelled.call(this, element, context);
+    },
+
+    applyExpression: function(expression, updateInterface)
+    {
+        expression = expression.trim();
+
+        if (!expression)
+            expression = null;
+
+        this.property.name = expression;
+        this.treeOutline.section.updateExpression(this, expression);
+    },
+
+    __proto__: WebInspector.ObjectPropertyTreeElement.prototype
+}
+
+
+/**
+ * @constructor
+ * @extends {WebInspector.ObjectPropertyTreeElement}
+ * @param {WebInspector.RemoteObjectProperty} property
+ */
+WebInspector.WatchedPropertyTreeElement = function(property)
+{
+    WebInspector.ObjectPropertyTreeElement.call(this, property);
+}
+
+WebInspector.WatchedPropertyTreeElement.prototype = {
+    onattach: function()
+    {
+        WebInspector.ObjectPropertyTreeElement.prototype.onattach.call(this);
+        if (this.hasChildren && this.propertyPath() in this.treeOutline.section._expandedProperties)
+            this.expand();
+    },
+
+    onexpand: function()
+    {
+        WebInspector.ObjectPropertyTreeElement.prototype.onexpand.call(this);
+        this.treeOutline.section._expandedProperties[this.propertyPath()] = true;
+    },
+
+    oncollapse: function()
+    {
+        WebInspector.ObjectPropertyTreeElement.prototype.oncollapse.call(this);
+        delete this.treeOutline.section._expandedProperties[this.propertyPath()];
+    },
+
+    __proto__: WebInspector.ObjectPropertyTreeElement.prototype
+}
diff --git a/Source/devtools/front_end/WorkerManager.js b/Source/devtools/front_end/WorkerManager.js
new file mode 100644
index 0000000..5172853
--- /dev/null
+++ b/Source/devtools/front_end/WorkerManager.js
@@ -0,0 +1,298 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.Object}
+ */
+WebInspector.WorkerManager = function()
+{
+    this._workerIdToWindow = {};
+    InspectorBackend.registerWorkerDispatcher(new WebInspector.WorkerDispatcher(this));
+}
+
+WebInspector.WorkerManager.isWorkerFrontend = function()
+{
+    return !!WebInspector.queryParamsObject["dedicatedWorkerId"] ||
+           !!WebInspector.queryParamsObject["isSharedWorker"];
+}
+
+WebInspector.WorkerManager.isDedicatedWorkerFrontend = function()
+{
+    return !!WebInspector.queryParamsObject["dedicatedWorkerId"];
+}
+
+WebInspector.WorkerManager.loaded = function()
+{
+    var workerId = WebInspector.queryParamsObject["dedicatedWorkerId"];
+    if (workerId)
+        WebInspector.WorkerManager._initializeDedicatedWorkerFrontend(workerId);
+    else
+        WebInspector.workerManager = new WebInspector.WorkerManager();
+}
+
+WebInspector.WorkerManager.loadCompleted = function()
+{
+    // Make sure script execution of dedicated worker is resumed and then paused
+    // on the first script statement in case we autoattached to it.
+    if (WebInspector.queryParamsObject["workerPaused"]) {
+        DebuggerAgent.pause();
+        RuntimeAgent.run(calculateTitle);
+    } else if (WebInspector.WorkerManager.isWorkerFrontend())
+        calculateTitle();
+
+    function calculateTitle()
+    {
+        WebInspector.WorkerManager._calculateWorkerInspectorTitle();
+    }
+
+    if (WebInspector.workerManager)
+        WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, WebInspector.workerManager._mainFrameNavigated, WebInspector.workerManager);
+}
+
+WebInspector.WorkerManager._initializeDedicatedWorkerFrontend = function(workerId)
+{
+    function receiveMessage(event)
+    {
+        var message = event.data;
+        InspectorBackend.dispatch(message);
+    }
+    window.addEventListener("message", receiveMessage, true);
+
+
+    InspectorBackend.sendMessageObjectToBackend = function(message)
+    {
+        window.opener.postMessage({workerId: workerId, command: "sendMessageToBackend", message: message}, "*");
+    }
+}
+
+WebInspector.WorkerManager._calculateWorkerInspectorTitle = function()
+{
+    var expression = "location.href";
+    if (WebInspector.queryParamsObject["isSharedWorker"])
+        expression += " + (this.name ? ' (' + this.name + ')' : '')";
+    RuntimeAgent.evaluate.invoke({expression:expression, doNotPauseOnExceptionsAndMuteConsole:true, returnByValue: true}, evalCallback.bind(this));
+    
+    /**
+     * @param {?Protocol.Error} error
+     * @param {RuntimeAgent.RemoteObject} result
+     * @param {boolean=} wasThrown
+     */
+    function evalCallback(error, result, wasThrown)
+    {
+        if (error || wasThrown) {
+            console.error(error);
+            return;
+        }
+        InspectorFrontendHost.inspectedURLChanged(result.value);
+    }
+}
+
+WebInspector.WorkerManager.Events = {
+    WorkerAdded: "worker-added",
+    WorkerRemoved: "worker-removed",
+    WorkersCleared: "workers-cleared",
+}
+
+WebInspector.WorkerManager.prototype = {
+    _workerCreated: function(workerId, url, inspectorConnected)
+     {
+        if (inspectorConnected)
+            this._openInspectorWindow(workerId, true);
+        this.dispatchEventToListeners(WebInspector.WorkerManager.Events.WorkerAdded, {workerId: workerId, url: url, inspectorConnected: inspectorConnected});
+     },
+
+    _workerTerminated: function(workerId)
+     {
+        this.closeWorkerInspector(workerId);
+        this.dispatchEventToListeners(WebInspector.WorkerManager.Events.WorkerRemoved, workerId);
+     },
+
+    _sendMessageToWorkerInspector: function(workerId, message)
+    {
+        var workerInspectorWindow = this._workerIdToWindow[workerId];
+        if (workerInspectorWindow)
+            workerInspectorWindow.postMessage(message, "*");
+    },
+
+    openWorkerInspector: function(workerId)
+    {
+        var existingInspector = this._workerIdToWindow[workerId];
+        if (existingInspector) {
+            existingInspector.focus();
+            return;
+        }
+
+        this._openInspectorWindow(workerId, false);
+        WorkerAgent.connectToWorker(workerId);
+    },
+
+    _openInspectorWindow: function(workerId, workerIsPaused)
+    {
+        var search = window.location.search;
+        var hash = window.location.hash;
+        var url = window.location.href;
+        // Make sure hash is in rear
+        url = url.replace(hash, "");
+        url += (search ? "&dedicatedWorkerId=" : "?dedicatedWorkerId=") + workerId;
+        if (workerIsPaused)
+            url += "&workerPaused=true";
+        url = url.replace("docked=true&", "");
+        url += hash;
+        var width = WebInspector.settings.workerInspectorWidth.get();
+        var height = WebInspector.settings.workerInspectorHeight.get();
+        // Set location=0 just to make sure the front-end will be opened in a separate window, not in new tab.
+        var workerInspectorWindow = window.open(url, undefined, "location=0,width=" + width + ",height=" + height);
+        workerInspectorWindow.addEventListener("resize", this._onWorkerInspectorResize.bind(this, workerInspectorWindow), false);
+        this._workerIdToWindow[workerId] = workerInspectorWindow;
+        workerInspectorWindow.addEventListener("beforeunload", this._workerInspectorClosing.bind(this, workerId), true);
+
+        // Listen to beforeunload in detached state and to the InspectorClosing event in case of attached inspector.
+        window.addEventListener("beforeunload", this._pageInspectorClosing.bind(this), true);
+        WebInspector.notifications.addEventListener(WebInspector.Events.InspectorClosing, this._pageInspectorClosing, this);
+    },
+
+    closeWorkerInspector: function(workerId)
+    {
+        var workerInspectorWindow = this._workerIdToWindow[workerId];
+        if (workerInspectorWindow)
+            workerInspectorWindow.close();
+    },
+
+    _mainFrameNavigated: function(event)
+    {
+        for (var workerId in this._workerIdToWindow)
+            this.closeWorkerInspector(workerId);
+        this.dispatchEventToListeners(WebInspector.WorkerManager.Events.WorkersCleared);
+    },
+
+    _pageInspectorClosing: function()
+    {
+        this._ignoreWorkerInspectorClosing = true;
+        for (var workerId in this._workerIdToWindow) {
+            this._workerIdToWindow[workerId].close();
+            WorkerAgent.disconnectFromWorker(parseInt(workerId, 10));
+        }
+    },
+
+    _onWorkerInspectorResize: function(workerInspectorWindow)
+    {
+        var doc = workerInspectorWindow.document;
+        WebInspector.settings.workerInspectorWidth.set(doc.width);
+        WebInspector.settings.workerInspectorHeight.set(doc.height);
+    },
+
+    _workerInspectorClosing: function(workerId, event)
+    {
+        if (event.target.location.href === "about:blank")
+            return;
+        if (this._ignoreWorkerInspectorClosing)
+            return;
+        delete this._workerIdToWindow[workerId];
+        WorkerAgent.disconnectFromWorker(workerId);
+    },
+
+    _disconnectedFromWorker: function()
+    {
+        var screen = new WebInspector.WorkerTerminatedScreen();
+        WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, screen.hide, screen);
+        screen.showModal();
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WorkerAgent.Dispatcher}
+ */
+WebInspector.WorkerDispatcher = function(workerManager)
+{
+    this._workerManager = workerManager;
+    window.addEventListener("message", this._receiveMessage.bind(this), true);
+}
+
+WebInspector.WorkerDispatcher.prototype = {
+    _receiveMessage: function(event)
+    {
+        var workerId = event.data["workerId"];
+        workerId = parseInt(workerId, 10);
+        var command = event.data.command;
+        var message = event.data.message;
+
+        if (command == "sendMessageToBackend")
+            WorkerAgent.sendMessageToWorker(workerId, message);
+    },
+
+    workerCreated: function(workerId, url, inspectorConnected)
+    {
+        this._workerManager._workerCreated(workerId, url, inspectorConnected);
+    },
+
+    workerTerminated: function(workerId)
+    {
+        this._workerManager._workerTerminated(workerId);
+    },
+
+    dispatchMessageFromWorker: function(workerId, message)
+    {
+        this._workerManager._sendMessageToWorkerInspector(workerId, message);
+    },
+
+    disconnectedFromWorker: function()
+    {
+        this._workerManager._disconnectedFromWorker();
+    }
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.HelpScreen}
+ */
+WebInspector.WorkerTerminatedScreen = function()
+{
+    WebInspector.HelpScreen.call(this, WebInspector.UIString("Inspected worker terminated"));
+    var p = this.contentElement.createChild("p");
+    p.addStyleClass("help-section");
+    p.textContent = WebInspector.UIString("Inspected worker has terminated. Once it restarts we will attach to it automatically.");
+}
+
+WebInspector.WorkerTerminatedScreen.prototype = {
+    /**
+     * @override
+     */
+    willHide: function()
+    {
+        WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this.hide, this);
+        WebInspector.HelpScreen.prototype.willHide.call(this);
+    },
+
+    __proto__: WebInspector.HelpScreen.prototype
+}
diff --git a/Source/devtools/front_end/WorkersSidebarPane.js b/Source/devtools/front_end/WorkersSidebarPane.js
new file mode 100644
index 0000000..a6c8938
--- /dev/null
+++ b/Source/devtools/front_end/WorkersSidebarPane.js
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.Worker = function(id, url, shared)
+{
+    this.id = id;
+    this.url = url;
+    this.shared = shared;
+}
+
+/**
+ * @constructor
+ * @extends {WebInspector.SidebarPane}
+ */
+WebInspector.WorkersSidebarPane = function(workerManager)
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Workers"));
+
+    this._enableWorkersCheckbox = new WebInspector.Checkbox(
+        WebInspector.UIString("Pause on start"),
+        "sidebar-label",
+        WebInspector.UIString("Automatically attach to new workers and pause them. Enabling this option will force opening inspector for all new workers."));
+    this._enableWorkersCheckbox.element.id = "pause-workers-checkbox";
+    this.bodyElement.appendChild(this._enableWorkersCheckbox.element);
+    this._enableWorkersCheckbox.addEventListener(this._autoattachToWorkersClicked.bind(this));
+    this._enableWorkersCheckbox.checked = false;
+
+    var note = this.bodyElement.createChild("div");
+    note.id = "shared-workers-list";
+    note.addStyleClass("sidebar-label")
+    note.textContent = WebInspector.UIString("Shared workers can be inspected in the Task Manager");
+
+    var separator = this.bodyElement.createChild("div", "sidebar-separator");
+    separator.textContent = WebInspector.UIString("Dedicated worker inspectors");
+
+    this._workerListElement = document.createElement("ol");
+    this._workerListElement.tabIndex = 0;
+    this._workerListElement.addStyleClass("properties-tree");
+    this._workerListElement.addStyleClass("sidebar-label");
+    this.bodyElement.appendChild(this._workerListElement);
+
+    this._idToWorkerItem = {};
+    this._workerManager = workerManager;
+
+    workerManager.addEventListener(WebInspector.WorkerManager.Events.WorkerAdded, this._workerAdded, this);
+    workerManager.addEventListener(WebInspector.WorkerManager.Events.WorkerRemoved, this._workerRemoved, this);
+    workerManager.addEventListener(WebInspector.WorkerManager.Events.WorkersCleared, this._workersCleared, this);
+}
+
+WebInspector.WorkersSidebarPane.prototype = {
+    _workerAdded: function(event)
+    {
+        this._addWorker(event.data.workerId, event.data.url, event.data.inspectorConnected);
+    },
+
+    _workerRemoved: function(event)
+    {
+        var workerItem = this._idToWorkerItem[event.data];
+        delete this._idToWorkerItem[event.data];
+        workerItem.parentElement.removeChild(workerItem);
+    },
+
+    _workersCleared: function(event)
+    {
+        this._idToWorkerItem = {};
+        this._workerListElement.removeChildren();
+    },
+
+    _addWorker: function(workerId, url, inspectorConnected)
+    {
+        var item = this._workerListElement.createChild("div", "dedicated-worker-item");
+        var link = item.createChild("a");
+        link.textContent = url;
+        link.href = "#";
+        link.target = "_blank";
+        link.addEventListener("click", this._workerItemClicked.bind(this, workerId), true);
+        this._idToWorkerItem[workerId] = item;
+    },
+
+    _workerItemClicked: function(workerId, event)
+    {
+        event.preventDefault();
+        this._workerManager.openWorkerInspector(workerId);
+    },
+
+    _autoattachToWorkersClicked: function(event)
+    {
+        WorkerAgent.setAutoconnectToWorkers(this._enableWorkersCheckbox.checked);
+    },
+
+    __proto__: WebInspector.SidebarPane.prototype
+}
diff --git a/Source/devtools/front_end/Workspace.js b/Source/devtools/front_end/Workspace.js
new file mode 100644
index 0000000..64aaf80
--- /dev/null
+++ b/Source/devtools/front_end/Workspace.js
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+/**
+ * @constructor
+ */
+WebInspector.WorkspaceController = function(workspace)
+{
+    this._workspace = workspace;
+    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._inspectedURLChanged, this);
+}
+
+WebInspector.WorkspaceController.prototype = {
+    /**
+     * @param {WebInspector.Event} event
+     */
+    _inspectedURLChanged: function(event)
+    {
+        WebInspector.Revision.filterOutStaleRevisions();
+    }
+}
+
+/**
+ * @constructor
+ * @param {Array.<string>} path
+ * @param {string} originURL
+ * @param {string} url
+ * @param {WebInspector.ResourceType} contentType
+ * @param {boolean} isEditable
+ * @param {boolean=} isContentScript
+ */
+WebInspector.FileDescriptor = function(path, originURL, url, contentType, isEditable, isContentScript)
+{
+    this.path = path;
+    this.originURL = originURL;
+    this.url = url;
+    this.contentType = contentType;
+    this.isEditable = isEditable;
+    this.isContentScript = isContentScript || false;
+}
+
+/**
+ * @interface
+ * @extends {WebInspector.EventTarget}
+ */
+WebInspector.ProjectDelegate = function() { }
+
+WebInspector.ProjectDelegate.Events = {
+    FileAdded: "FileAdded",
+    FileRemoved: "FileRemoved",
+    Reset: "Reset",
+}
+
+WebInspector.ProjectDelegate.prototype = {
+    /**
+     * @return {string}
+     */
+    id: function() { },
+
+    /**
+     * @return {string}
+     */
+    type: function() { },
+
+    /**
+     * @return {string}
+     */
+    displayName: function() { }, 
+
+    /**
+     * @param {Array.<string>} path
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestFileContent: function(path, callback) { },
+
+    /**
+     * @return {boolean}
+     */
+    canSetFileContent: function() { },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {string} newContent
+     * @param {function(?string)} callback
+     */
+    setFileContent: function(path, newContent, callback) { },
+
+    /**
+     * @param {Array.<string>} path
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInFileContent: function(path, query, caseSensitive, isRegex, callback) { }
+}
+
+/**
+ * @type {?WebInspector.WorkspaceController}
+ */
+WebInspector.workspaceController = null;
+
+/**
+ * @param {WebInspector.Workspace} workspace
+ * @param {WebInspector.ProjectDelegate} projectDelegate
+ * @constructor
+ */
+WebInspector.Project = function(workspace, projectDelegate)
+{
+    /** @type {Object.<string, WebInspector.UISourceCode>} */
+    this._uiSourceCodes = {};
+    this._workspace = workspace;
+    this._projectDelegate = projectDelegate;
+    this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileAdded, this._fileAdded, this);
+    this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileRemoved, this._fileRemoved, this);
+    this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.Reset, this._reset, this);
+}
+
+WebInspector.Project.prototype = {
+    /**
+     * @return {string}
+     */
+    id: function()
+    {
+        return this._projectDelegate.id();
+    },
+
+    /**
+     * @return {string}
+     */
+    type: function()
+    {
+        return this._projectDelegate.type(); 
+    },
+
+    /**
+     * @return {string}
+     */
+    displayName: function() 
+    {
+        return this._projectDelegate.displayName(); 
+    },
+
+    /**
+     * @return {boolean}
+     */
+    isServiceProject: function()
+    {
+        return this._projectDelegate.type() === WebInspector.projectTypes.Debugger || this._projectDelegate.type() === WebInspector.projectTypes.LiveEdit;
+    },
+
+    _fileAdded: function(event)
+    {
+        var fileDescriptor = /** @type {WebInspector.FileDescriptor} */ (event.data);
+        var uiSourceCode = this.uiSourceCode(fileDescriptor.path);
+        if (uiSourceCode) {
+            // FIXME: Implement
+            return;
+        }
+
+        uiSourceCode = new WebInspector.UISourceCode(this, fileDescriptor.path, fileDescriptor.originURL, fileDescriptor.url, fileDescriptor.contentType, fileDescriptor.isEditable); 
+        uiSourceCode.isContentScript = fileDescriptor.isContentScript;
+        this._uiSourceCodes[uiSourceCode.path().join("/")] = uiSourceCode;
+        this._workspace.dispatchEventToListeners(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, uiSourceCode);
+    },
+
+    _fileRemoved: function(event)
+    {
+        var path = /** @type {Array.<string>} */ (event.data);
+        var uiSourceCode = this.uiSourceCode(path);
+        if (!uiSourceCode)
+            return;
+        delete this._uiSourceCodes[uiSourceCode.path().join("/")];
+        this._workspace.dispatchEventToListeners(WebInspector.UISourceCodeProvider.Events.UISourceCodeRemoved, uiSourceCode);
+    },
+
+    _reset: function()
+    {
+        this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectWillReset, this);
+        this._uiSourceCodes = {};
+    },
+
+    /**
+     * @param {Array.<string>} path
+     * @return {?WebInspector.UISourceCode}
+     */
+    uiSourceCode: function(path)
+    {
+        return this._uiSourceCodes[path.join("/")] || null;
+    },
+
+    /**
+     * @param {string} originURL
+     * @return {?WebInspector.UISourceCode}
+     */
+    uiSourceCodeForOriginURL: function(originURL)
+    {
+        for (var path in this._uiSourceCodes) {
+            var uiSourceCode = this._uiSourceCodes[path];
+            if (uiSourceCode.originURL() === originURL)
+                return uiSourceCode;
+        }
+        return null;
+    },
+
+    /**
+     * @return {Array.<WebInspector.UISourceCode>}
+     */
+    uiSourceCodes: function()
+    {
+        return Object.values(this._uiSourceCodes);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {function(?string,boolean,string)} callback
+     */
+    requestFileContent: function(uiSourceCode, callback)
+    {
+        this._projectDelegate.requestFileContent(uiSourceCode.path(), callback);
+    },
+
+    /**
+     * @return {boolean}
+     */
+    canSetFileContent: function()
+    {
+        return this._projectDelegate.canSetFileContent();
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {string} newContent
+     * @param {function(?string)} callback
+     */
+    setFileContent: function(uiSourceCode, newContent, callback)
+    {
+        this._projectDelegate.setFileContent(uiSourceCode.path(), newContent, callback);
+        this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: uiSourceCode, content: newContent });
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {string} query
+     * @param {boolean} caseSensitive
+     * @param {boolean} isRegex
+     * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
+     */
+    searchInFileContent: function(uiSourceCode, query, caseSensitive, isRegex, callback)
+    {
+        this._projectDelegate.searchInFileContent(uiSourceCode.path(), query, caseSensitive, isRegex, callback);
+    },
+
+    dispose: function()
+    {
+        this._projectDelegate.reset();
+    }
+}
+
+WebInspector.projectTypes = {
+    Debugger: "debugger",
+    LiveEdit: "liveedit",
+    Network: "network",
+    Snippets: "snippets",
+    FileSystem: "filesystem"
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.UISourceCodeProvider}
+ * @extends {WebInspector.Object}
+ * @param {WebInspector.FileMapping} fileMapping
+ * @param {WebInspector.FileSystemMapping} fileSystemMapping
+ */
+WebInspector.Workspace = function(fileMapping, fileSystemMapping)
+{
+    this._fileMapping = fileMapping;
+    this._fileSystemMapping = fileSystemMapping;
+    /** @type {!Object.<string, WebInspector.Project>} */
+    this._projects = {};
+}
+
+WebInspector.Workspace.Events = {
+    UISourceCodeContentCommitted: "UISourceCodeContentCommitted",
+    ProjectWillReset: "ProjectWillReset"
+}
+
+WebInspector.Workspace.prototype = {
+    /**
+     * @param {string} projectId
+     * @param {Array.<string>} path
+     * @return {?WebInspector.UISourceCode}
+     */
+    uiSourceCode: function(projectId, path)
+    {
+        var project = this._projects[projectId];
+        return project ? project.uiSourceCode(path) : null;
+    },
+
+    /**
+     * @param {string} originURL
+     * @return {?WebInspector.UISourceCode}
+     */
+    uiSourceCodeForOriginURL: function(originURL)
+    {
+        var networkProjects = this.projectsForType(WebInspector.projectTypes.Network)
+        for (var i = 0; i < networkProjects.length; ++i) {
+            var project = networkProjects[i];
+            var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
+            if (uiSourceCode)
+                return uiSourceCode;
+        }
+        return null;
+    },
+
+    /**
+     * @param {string} type
+     * @return {Array.<WebInspector.UISourceCode>}
+     */
+    uiSourceCodesForProjectType: function(type)
+    {
+        var result = [];
+        for (var projectName in this._projects) {
+            var project = this._projects[projectName];
+            if (project.type() === type)
+                result = result.concat(project.uiSourceCodes());
+        }
+        return result;
+    },
+
+    /**
+     * @param {WebInspector.ProjectDelegate} projectDelegate
+     * @return {WebInspector.Project}
+     */
+    addProject: function(projectDelegate)
+    {
+        var projectId = projectDelegate.id();
+        this._projects[projectId] = new WebInspector.Project(this, projectDelegate);
+        return this._projects[projectId];
+    },
+
+    /**
+     * @param {string} projectId
+     */
+    removeProject: function(projectId)
+    {
+        var project = this._projects[projectId];
+        if (!project)
+            return;
+        project.dispose();
+        delete this._projects[projectId];
+    },
+
+    /**
+     * @param {string} projectId
+     * @return {WebInspector.Project}
+     */
+    project: function(projectId)
+    {
+        return this._projects[projectId];
+    },
+
+    /**
+     * @return {Array.<WebInspector.Project>}
+     */
+    projects: function()
+    {
+        return Object.values(this._projects);
+    },
+
+    /**
+     * @param {string} type
+     * @return {Array.<WebInspector.Project>}
+     */
+    projectsForType: function(type)
+    {
+        function filterByType(project)
+        {
+            return project.type() === type;
+        }
+        return this.projects().filter(filterByType);
+    },
+
+    /**
+     * @return {Array.<WebInspector.UISourceCode>}
+     */
+    uiSourceCodes: function()
+    {
+        var result = [];
+        for (var projectId in this._projects) {
+            var project = this._projects[projectId];
+            result = result.concat(project.uiSourceCodes());
+        }
+        return result;
+    },
+
+    /**
+     * @param {string} url
+     * @return {boolean}
+     */
+    hasMappingForURL: function(url)
+    {
+        var entry = this._fileMapping.mappingEntryForURL(url);
+        if (!entry)
+            return false;
+        return !!this._fileSystemPathForEntry(entry);
+    },
+    
+    /**
+     * @param {WebInspector.FileMapping.Entry} entry
+     * @return {?string}
+     */
+    _fileSystemPathForEntry: function(entry)
+    {
+        return this._fileSystemMapping.fileSystemPathForPrefix(entry.pathPrefix);
+    },
+    /**
+     * @param {string} url
+     * @return {WebInspector.UISourceCode}
+     */
+    uiSourceCodeForURL: function(url)
+    {
+        var entry = this._fileMapping.mappingEntryForURL(url);
+        var fileSystemPath = entry ? this._fileSystemPathForEntry(entry) : null;
+        if (!fileSystemPath) {
+            var splittedURL = WebInspector.ParsedURL.splitURL(url);
+            var projectId = WebInspector.SimpleProjectDelegate.projectId(splittedURL[0], WebInspector.projectTypes.Network);
+            var path = WebInspector.SimpleWorkspaceProvider.pathForSplittedURL(splittedURL);
+            var project = this.project(projectId);
+            return project ? project.uiSourceCode(path) : null;
+        }
+
+        var projectId = WebInspector.FileSystemProjectDelegate.projectId(fileSystemPath);
+        var pathPrefix = entry.pathPrefix.substr(fileSystemPath.length + 1);
+        var path = pathPrefix + url.substr(entry.urlPrefix.length);
+        var project = this.project(projectId);
+        return project ? project.uiSourceCode(path.split("/")) : null;
+    },
+
+    /**
+     * @param {string} path
+     * @return {string}
+     */
+    urlForPath: function(path)
+    {
+        var entry = this._fileMapping.mappingEntryForPath(path);
+        if (!entry)
+            return "";
+        return entry.urlPrefix + path.substring(entry.pathPrefix.length);
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} networkUISourceCode
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     * @param {WebInspector.FileSystemWorkspaceProvider} fileSystemWorkspaceProvider
+     */
+    addMapping: function(networkUISourceCode, uiSourceCode, fileSystemWorkspaceProvider)
+    {
+        var url = networkUISourceCode.url;
+        var path = uiSourceCode.path();
+        var suffix = "";
+        for (var i = path.length - 1; i >= 0; --i) {
+            var nextSuffix = "/" + path[i] + suffix;
+            if (!url.endsWith(nextSuffix))
+                break;
+            suffix = nextSuffix;
+        }
+        var fileSystemPath = fileSystemWorkspaceProvider.fileSystemPath(uiSourceCode);
+        var filePath = "/" + path.join("/");
+        var pathPrefix = fileSystemPath + filePath.substr(0, filePath.length - suffix.length) + "/";
+        var urlPrefix = url.substr(0, url.length - suffix.length) + "/";
+
+        var entries = this._fileMapping.mappingEntries();
+        var entry = new WebInspector.FileMapping.Entry(urlPrefix, pathPrefix);
+        entries.push(entry);
+        this._fileMapping.setMappingEntries(entries);
+        WebInspector.suggestReload();
+    },
+
+    /**
+     * @param {WebInspector.UISourceCode} uiSourceCode
+     */
+    removeMapping: function(uiSourceCode)
+    {
+        var entry = this._fileMapping.mappingEntryForURL(uiSourceCode.url);
+        var entries = this._fileMapping.mappingEntries();
+        entries.remove(entry);
+        this._fileMapping.setMappingEntries(entries);
+        WebInspector.suggestReload();
+    },
+
+    __proto__: WebInspector.Object.prototype
+}
+
+/**
+ * @type {?WebInspector.Workspace}
+ */
+WebInspector.workspace = null;
diff --git a/Source/devtools/front_end/auditsPanel.css b/Source/devtools/front_end/auditsPanel.css
new file mode 100644
index 0000000..a25421b
--- /dev/null
+++ b/Source/devtools/front_end/auditsPanel.css
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 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:
+ *
+ *     * 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.
+ */
+
+.audits-sidebar-tree-item .icon {
+    content: url(Images/resourcesTimeGraphIcon.png);
+}
+
+.audit-result-sidebar-tree-item .icon {
+    content: url(Images/resourceDocumentIcon.png);
+}
+
+.audit-launcher-view {
+    z-index: 1000;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-color: white;
+    font-size: 13px;
+    overflow-x: hidden;
+    overflow-y: overlay;
+    display: none;
+}
+
+.audit-launcher-view.visible {
+    display: block;
+}
+
+.audit-launcher-view .audit-launcher-view-content {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    padding: 0 0 0 16px;
+    white-space: nowrap;
+    display: -webkit-flex;
+    text-align: left;
+    -webkit-flex-direction: column;
+}
+
+.audit-launcher-view h1 {
+    padding-top: 15px;
+    -webkit-flex: none;
+}
+
+.audit-launcher-view h1.no-audits {
+    text-align: center;
+    font-style: italic;
+    position: relative;
+    left: -8px;
+}
+
+.audit-launcher-view div.button-container {
+    width: 100%;
+    padding: 16px 0;
+    -webkit-flex: none;
+}
+
+.audit-launcher-view div.button-container > button {
+    -webkit-align-self: flex-start;
+}
+
+.audit-launcher-view fieldset.audit-categories-container {
+    position: relative;
+    top: 11px;
+    left: 0;
+    width: 100%;
+    overflow-y: auto;
+    border: 0 none;
+    -webkit-flex: none;
+}
+
+.audit-launcher-view button {
+    margin: 0 5px 0 0;
+}
+
+.audit-launcher-view button:active {
+    background-color: rgb(215, 215, 215);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239)));
+}
+
+.panel-enabler-view.audit-launcher-view label {
+    padding: 0 0 5px 0;
+    margin: 0;
+    -webkit-flex: none;
+}
+
+.panel-enabler-view.audit-launcher-view label.disabled {
+    color: rgb(130, 130, 130);
+}
+
+.audit-launcher-view input[type="checkbox"] {
+    margin-left: 0;
+}
+
+.audit-result-view {
+    overflow: auto;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    display: none;
+}
+
+.audit-result-view.visible {
+    display: block;
+}
+
+.audit-result-view .severity-severe,
+.audit-result-view .severity-warning,
+.audit-result-view .severity-info {
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    display: inline-block;
+    width: 10px;
+    height: 10px;
+    float: left;
+    margin-left: -28px;
+    margin-top: 3px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.audit-result-view .severity-severe,
+.audit-result-view .severity-warning,
+.audit-result-view .severity-info {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.audit-result-view .severity-severe {
+    background-position: -224px -96px;
+}
+
+.audit-result-view .severity-warning {
+    background-position: -246px -96px;
+}
+
+.audit-result-view .severity-info {
+    background-position: -235px -96px;
+}
+
+.audit-result-tree li.parent::before {
+    -webkit-user-select: none;
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    opacity: 0.5;
+    float: left;
+    width: 8px;
+    height: 10px;
+    content: "a";
+    color: transparent;
+    margin-left: 3px;
+    margin-right: 4px;
+    position: relative;
+    top: 2px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.audit-result-tree li.parent::before {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.audit-result-tree li.parent::before {
+    background-position: -4px -96px;
+}
+
+.audit-result-tree li.parent.expanded::before {
+    background-position: -20px -96px;
+}
+
+.audit-result-tree {
+    line-height: 16px;
+    -webkit-user-select: text;
+}
+
+.audit-result-tree > ol {
+    position: relative;
+    padding: 2px 6px !important;
+    margin: 0;
+    cursor: default;
+    min-width: 100%;
+}
+
+.audit-result-tree, .audit-result-tree ol {
+    list-style-type: none;
+    -webkit-padding-start: 12px;
+    margin: 0;
+}
+
+.audit-result-tree ol.outline-disclosure {
+    -webkit-padding-start: 0;
+}
+
+.audit-result-tree .section .header {
+    padding-left: 13px;
+}
+
+.audit-result-tree .section .header::before {
+    left: 2px;
+}
+
+.audit-result-tree li {
+    padding: 0 0 0 14px;
+    margin-top: 1px;
+    margin-bottom: 1px;
+    word-wrap: break-word;
+    margin-left: -2px;
+}
+
+.audit-result-tree li.parent {
+    margin-left: -12px
+}
+
+.audit-result-tree ol.children {
+    display: none;
+}
+
+.audit-result-tree ol.children.expanded {
+    display: block;
+}
+
+.audit-result {
+    font-weight: bold;
+}
+
+.audit-launcher-view .progress-bar-container {
+    display: inline-block;
+}
+
+.audit-launcher-view .progress-bar-container .progress-bar-stop-button {
+    display: none;
+}
diff --git a/Source/devtools/front_end/breadcrumbList.css b/Source/devtools/front_end/breadcrumbList.css
new file mode 100644
index 0000000..729c134
--- /dev/null
+++ b/Source/devtools/front_end/breadcrumbList.css
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+.crumbs {
+    display: inline-block;
+    pointer-events: auto;
+    cursor: default;
+    font-size: 11px;
+    line-height: 19px;
+    text-shadow: rgba(255, 255, 255, 0.75) 0 1px 0;
+    color: rgb(20, 20, 20);
+    margin-left: -1px;
+    margin-top: -1px;
+    padding-right: 12px;
+}
+
+.crumbs .crumb {
+    height: 24px;
+    border-width: 0 12px 0 2px;
+    -webkit-border-image: url(Images/segment.png) 0 12 0 2;
+    margin-right: -3px;
+    padding-left: 6px;
+    padding-right: 2px;
+    white-space: nowrap;
+    line-height: 23px;
+    float: right;
+}
+
+.crumbs .crumb.collapsed > * {
+    display: none;
+}
+
+.crumbs .crumb.collapsed::before {
+    content: "\2026";
+    font-weight: bold;
+}
+
+.crumbs .crumb.compact .extra {
+    display: none;
+}
+
+.crumbs .crumb.start {
+    padding-left: 7px;
+}
+
+.crumbs .crumb.end {
+    border-width: 0 2px 0 2px;
+    padding-right: 6px;
+    -webkit-border-image: url(Images/segmentEnd.png) 0 2 0 2;
+}
+
+.crumbs .crumb.selected {
+    -webkit-border-image: url(Images/segmentSelected.png) 0 12 0 2;
+    color: white;
+    text-shadow: rgba(255, 255, 255, 0.5) 0 0px 0;
+}
+
+.crumbs .crumb.selected:hover {
+    -webkit-border-image: url(Images/segmentSelected.png) 0 12 0 2;
+}
+
+.crumbs .crumb.selected.end, .crumbs .crumb.selected.end:hover {
+    -webkit-border-image: url(Images/segmentSelectedEnd.png) 0 2 0 2;
+}
+
+.crumbs .crumb:hover {
+    -webkit-border-image: url(Images/segmentHover.png) 0 12 0 2;
+    color: #222;
+}
+
+.crumbs .crumb.end:hover {
+    -webkit-border-image: url(Images/segmentHoverEnd.png) 0 2 0 2;
+}
diff --git a/Source/devtools/front_end/breakpointsList.css b/Source/devtools/front_end/breakpointsList.css
new file mode 100644
index 0000000..78edf83
--- /dev/null
+++ b/Source/devtools/front_end/breakpointsList.css
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+.sidebar-pane > .body .breakpoint-condition {
+    display: block;
+    margin-top: 4px;
+    margin-bottom: 4px;
+    margin-left: 23px;
+    margin-right: 8px;
+}
+
+#breakpoint-condition-input {
+    display: block;
+    margin-left: 0;
+    margin-right: 0;
+    outline: none !important;
+    border: 1px solid rgb(66%, 66%, 66%);
+}
+
+ol.breakpoint-list {
+    -webkit-padding-start: 0;
+    list-style: none;
+    margin: 0;
+}
+
+.breakpoints-list-deactivated {
+    background-color: rgb(245, 245, 245);
+    opacity: 0.3;
+}
+
+.breakpoint-list li {
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    padding: 2px 0;
+}
+
+.breakpoint-list li:hover {
+    background-color: rgba(56, 121, 217, 0.2);
+}
+
+.breakpoint-list .checkbox-elem {
+    font-size: 10px;
+    margin: 0 4px;
+    vertical-align: top;
+    position: relative;
+    z-index: 1;
+    top: 3px;
+}
+
+.breakpoint-list .source-text {
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    margin: 2px 0 0px 20px;
+}
+
+.sidebar-pane .breakpoint-hit {
+    background-color: rgb(255, 255, 194);
+}
+
+li.breakpoint-hit .breakpoint-hit-marker {
+    background-color: rgb(255, 255, 194);
+    height: 18px;
+    left: 0px;
+    margin-top: -16px;
+    position: absolute;
+    right: 0px;
+    z-index: -1;
+}
+
+.event-listener-breakpoints.properties-tree .children li {
+    margin-left: 12px;
+    height: 16px;
+}
+
+.event-listener-breakpoints .checkbox-elem {
+    float: left;
+    top: -2px;
+    position: relative;
+    left: -1px;
+    font-size: 10px;
+}
diff --git a/Source/devtools/front_end/buildSystemOnly.js b/Source/devtools/front_end/buildSystemOnly.js
new file mode 100644
index 0000000..7be07f7
--- /dev/null
+++ b/Source/devtools/front_end/buildSystemOnly.js
@@ -0,0 +1,7 @@
+/**
+ * This flag notifies inspector that it was deployed with the help
+ * of a build system. Build system flattenes all css and js files,
+ * so in this case inspector has to correct paths for dynamic resource loading.
+ */
+window.flattenImports = true;
+window.DEBUG = false;
\ No newline at end of file
diff --git a/Source/devtools/front_end/canvasProfiler.css b/Source/devtools/front_end/canvasProfiler.css
new file mode 100644
index 0000000..fca5e04
--- /dev/null
+++ b/Source/devtools/front_end/canvasProfiler.css
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+.canvas-profile-view {
+    overflow: hidden;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+#canvas-replay-image-container {
+    text-align: center;
+    background-color: black;
+    overflow: hidden;
+    padding: 5px 5px 10px 5px;
+    color: white;
+}
+
+.canvas-replay-image {
+    zoom: 100;
+    height: auto;
+    width: auto;
+    max-width: 100%;
+    max-height: 100%;
+    margin: auto;
+    display: block;
+}
+
+.canvas-debug-info {
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 6px;
+}
+
+.canvas-spinner-icon {
+    content: url(Images/spinnerActiveSelected.gif);
+    position: absolute;
+    width: 16px;
+    right: 4px;
+    bottom: 4px;
+}
+
+.canvas-replay-log {
+    position: absolute;
+    top: 24px;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.canvas-replay-first-step .glyph {
+    -webkit-mask-position: 0 -72px;
+    -webkit-transform: rotate(180deg);
+    top: 0;
+    bottom: 0;
+}
+.canvas-replay-next-step .glyph {
+    -webkit-mask-position: -64px -72px;
+}
+.canvas-replay-prev-step .glyph {
+    -webkit-mask-position: -96px -72px;
+}
+.canvas-replay-last-step .glyph {
+    -webkit-mask-position: 0 -72px;
+}
+.canvas-replay-prev-draw .glyph {
+    -webkit-mask-position: -128px -72px;
+    -webkit-transform: scaleX(-1);
+}
+.canvas-replay-next-draw .glyph {
+    -webkit-mask-position: -128px -72px;
+}
diff --git a/Source/devtools/front_end/cm/LICENSE b/Source/devtools/front_end/cm/LICENSE
new file mode 100644
index 0000000..3916e96
--- /dev/null
+++ b/Source/devtools/front_end/cm/LICENSE
@@ -0,0 +1,23 @@
+Copyright (C) 2012 by Marijn Haverbeke <marijnh@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+Please note that some subdirectories of the CodeMirror distribution
+include their own LICENSE files, and are released under different
+licences.
diff --git a/Source/devtools/front_end/cm/closebrackets.js b/Source/devtools/front_end/cm/closebrackets.js
new file mode 100644
index 0000000..b46caca
--- /dev/null
+++ b/Source/devtools/front_end/cm/closebrackets.js
@@ -0,0 +1,52 @@
+(function() {
+  var DEFAULT_BRACKETS = "()[]{}''\"\"";
+  var SPACE_CHAR_REGEX = /\s/;
+
+  CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
+    var wasOn = old && old != CodeMirror.Init;
+    if (val && !wasOn)
+      cm.addKeyMap(buildKeymap(typeof val == "string" ? val : DEFAULT_BRACKETS));
+    else if (!val && wasOn)
+      cm.removeKeyMap("autoCloseBrackets");
+  });
+
+  function buildKeymap(pairs) {
+    var map = {
+      name : "autoCloseBrackets",
+      Backspace: function(cm) {
+        if (cm.somethingSelected()) return CodeMirror.Pass;
+        var cur = cm.getCursor(), line = cm.getLine(cur.line);
+        if (cur.ch && cur.ch < line.length &&
+            pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0)
+          cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1));
+        else
+          return CodeMirror.Pass;
+      }
+    };
+    var closingBrackets = [];
+    for (var i = 0; i < pairs.length; i += 2) (function(left, right) {
+      if (left != right) closingBrackets.push(right);
+      function surround(cm) {
+          var selection = cm.getSelection();
+          cm.replaceSelection(left + selection + right);
+      }
+      function maybeOverwrite(cm) {
+        var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1));
+        if (ahead != right || cm.somethingSelected()) return CodeMirror.Pass;
+        else cm.execCommand("goCharRight");
+      }
+      map["'" + left + "'"] = function(cm) {
+        if (cm.somethingSelected()) return surround(cm);
+        if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return;
+        var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1);
+        var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch);
+        if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar))
+          cm.replaceSelection(left + right, {head: ahead, anchor: ahead});
+        else
+          return CodeMirror.Pass;
+      };
+      if (left != right) map["'" + right + "'"] = maybeOverwrite;
+    })(pairs.charAt(i), pairs.charAt(i + 1));
+    return map;
+  }
+})();
diff --git a/Source/devtools/front_end/cm/cmdevtools.css b/Source/devtools/front_end/cm/cmdevtools.css
new file mode 100644
index 0000000..7026b63
--- /dev/null
+++ b/Source/devtools/front_end/cm/cmdevtools.css
@@ -0,0 +1,156 @@
+.CodeMirror * {
+  -webkit-box-sizing: content-box;
+}
+
+.CodeMirror {
+  line-height: 1.2em !important;
+  height: 100% !important;
+  background-color: transparent !important;
+}
+
+.CodeMirror .source-frame-eval-expression {
+  outline: 0px;
+  border: 1px solid rgb(163, 41, 34);
+  border-left-width: 0px;
+  border-right-width: 0px;
+}
+
+.CodeMirror .source-frame-eval-expression-end {
+  border-right-width: 1px;
+  margin-right: -1px;
+}
+
+.CodeMirror .source-frame-eval-expression-start {
+  border-left-width: 1px;
+  margin-left: -1px;
+}
+
+
+.CodeMirror-linenumber {
+  min-width: 22px !important;
+}
+
+.cm-highlight {
+  -webkit-animation: "fadeout" 2s 0s;
+}
+@-webkit-keyframes fadeout {
+    from {background-color: rgb(255, 255, 120); }
+    to { background-color: white; }
+}
+
+.cm-breakpoint .CodeMirror-linenumber {
+  color: white;
+  border-width: 1px 4px 1px 1px !important;
+  -webkit-border-image: url(Images/breakpoint2.png) 1 4 1 1;
+  margin: 0px 0px 0px 3px !important;
+  padding-right: 3px;
+  padding-left: 1px;
+  height: 10px;
+  line-height: 11px !important;
+}
+
+.cm-breakpoint.cm-breakpoint-conditional .CodeMirror-linenumber {
+  -webkit-border-image: url(Images/breakpointConditional2.png) 1 4 1 1;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+  .cm-breakpoint .CodeMirror-linenumber {
+    -webkit-border-image: url(Images/breakpoint2_2x.png) 2 8 2 2;
+  }
+  .cm-breakpoint.cm-breakpoint-conditional .CodeMirror-linenumber {
+    -webkit-border-image: url(Images/breakpointConditional2_2x.png) 2 8 2 2;
+  }
+}
+
+.cm-breakpoint-disabled .CodeMirror-linenumber {
+  opacity: 0.5;
+}
+
+.CodeMirror-matchingbracket {
+  border-bottom: 1px solid black;
+  color: #222 !important;
+}
+
+.CodeMirror-nonmatchingbracket {
+  color: #222 !important;
+}
+
+.cm-execution-line {
+    background-color: rgb(171, 191, 254) !important;
+    outline: 1px solid rgb(64, 115, 244);
+}
+
+.cm-execution-line .CodeMirror-linenumber {
+    border-right: 1px solid rgb(64, 115, 244);
+}
+
+.cm-token-highlight {
+    position: relative;
+}
+
+.cm-token-highlight:before {
+    position: absolute;
+    border: 1px solid gray;
+    border-radius: 3px;
+    top: 0px;
+    bottom: 0px;
+    left: 0px;
+    right: 0px;
+    content: "";
+}
+
+.cm-line-with-selection .cm-column-with-selection:before {
+    border: 0px;
+}
+
+.cm-s-web-inspector-js span.cm-keyword {color: rgb(170, 13, 145);}
+.cm-s-web-inspector-js span.cm-number {color: rgb(28, 0, 207);}
+.cm-s-web-inspector-js span.cm-comment {color: rgb(0, 116, 0);}
+.cm-s-web-inspector-js span.cm-string {color: rgb(196, 26, 22);}
+.cm-s-web-inspector-js span.cm-string-2 {color: rgb(196, 26, 22);}
+
+.cm-s-web-inspector-css span.cm-keyword { color: rgb(7, 144, 154);}
+.cm-s-web-inspector-css span.cm-number {color: rgb(50, 0, 255);}
+.cm-s-web-inspector-css span.cm-comment {color: rgb(0, 116, 0);}
+.cm-s-web-inspector-css span.cm-meta {color: rgb(200, 0, 0);}
+.cm-s-web-inspector-css span.cm-atom {color: rgb(7, 144, 154);}
+.cm-s-web-inspector-css span.cm-string {color: rgb(7, 144, 154);}
+.cm-s-web-inspector-css span.cm-string-2 {color: rgb(7, 144, 154);}
+.cm-s-web-inspector-css span.cm-link {color: rgb(7, 144, 154);}
+.cm-s-web-inspector-css span.cm-variable {color: rgb(200, 0, 0);}
+.cm-s-web-inspector-css span.cm-property {color: rgb(200, 0, 0);}
+
+.cm-s-web-inspector-html span.cm-meta {color: rgb(192, 192, 192);}
+.cm-s-web-inspector-html span.cm-comment {color: rgb(35, 110, 37);}
+.cm-s-web-inspector-html span.cm-string {color: rgb(26, 26, 166);}
+.cm-s-web-inspector-html span.cm-tag {color: rgb(136, 18, 128);}
+.cm-s-web-inspector-html span.cm-attribute {color: rgb(153, 69, 0);}
+.cm-s-web-inspector-html span.cm-link {color: #00e;}
+
+.CodeMirror .webkit-html-message-bubble {
+    -webkit-box-shadow: black 0px 2px 5px;
+    -webkit-border-radius: 9px;
+    -webkit-border-fit: lines;
+    font-size: 10px;
+    font-family: Lucida Grande, sans-serif;
+    font-weight: bold;
+    margin: 3px 10px;
+    padding: 1px 2px 0;
+    z-index: 5;
+}
+
+.CodeMirror .webkit-html-message-bubble img {
+    position: relative;
+    top: 1px;
+    margin-right: 2px;
+}
+
+.CodeMirror .webkit-html-warning-message {
+    background-color: rgb(100%, 62%, 42%);
+    border: 2px solid rgb(100%, 52%, 21%);
+}
+
+.CodeMirror .webkit-html-error-message {
+    background-color: rgb(100%, 42%, 42%);
+    border: 2px solid rgb(100%, 31%, 31%);
+}
diff --git a/Source/devtools/front_end/cm/codemirror.css b/Source/devtools/front_end/cm/codemirror.css
new file mode 100644
index 0000000..0b53704
--- /dev/null
+++ b/Source/devtools/front_end/cm/codemirror.css
@@ -0,0 +1,246 @@
+/* BASICS */
+
+.CodeMirror {
+  /* Set height, width, borders, and global font properties here */
+  font-family: monospace;
+  height: 300px;
+}
+.CodeMirror-scroll {
+  /* Set scrolling behaviour here */
+  overflow: auto;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+  padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+  padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler {
+  background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+  border-right: 1px solid #ddd;
+  background-color: #f7f7f7;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+  padding: 0 3px 0 5px;
+  min-width: 20px;
+  text-align: right;
+  color: #999;
+}
+
+/* CURSOR */
+
+.CodeMirror div.CodeMirror-cursor {
+  border-left: 1px solid black;
+  z-index: 3;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+  border-left: 1px solid silver;
+}
+.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
+  width: auto;
+  border: 0;
+  background: #7e7;
+  z-index: 1;
+}
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
+
+.cm-tab { display: inline-block; }
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable {color: black;}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-property {color: black;}
+.cm-s-default .cm-operator {color: black;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-error {color: #f00;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+
+.cm-invalidchar {color: #f00;}
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+   the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+  line-height: 1;
+  position: relative;
+  overflow: hidden;
+  background: white;
+  color: black;
+}
+
+.CodeMirror-scroll {
+  /* 30px is the magic margin used to hide the element's real scrollbars */
+  /* See overflow: hidden in .CodeMirror */
+  margin-bottom: -30px; margin-right: -30px;
+  padding-bottom: 30px; padding-right: 30px;
+  height: 100%;
+  outline: none; /* Prevent dragging from highlighting the element */
+  position: relative;
+}
+.CodeMirror-sizer {
+  position: relative;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+   before actuall scrolling happens, thus preventing shaking and
+   flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
+  position: absolute;
+  z-index: 6;
+  display: none;
+}
+.CodeMirror-vscrollbar {
+  right: 0; top: 0;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+  bottom: 0; left: 0;
+  overflow-y: hidden;
+  overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+  right: 0; bottom: 0;
+  z-index: 6;
+}
+
+.CodeMirror-gutters {
+  position: absolute; left: 0; top: 0;
+  height: 100%;
+  padding-bottom: 30px;
+  z-index: 3;
+}
+.CodeMirror-gutter {
+  height: 100%;
+  padding-bottom: 30px;
+  margin-bottom: -32px;
+  display: inline-block;
+  /* Hack to make IE7 behave */
+  *zoom:1;
+  *display:inline;
+}
+.CodeMirror-gutter-elt {
+  position: absolute;
+  cursor: default;
+  z-index: 4;
+}
+
+.CodeMirror-lines {
+  cursor: text;
+}
+.CodeMirror pre {
+  /* Reset some styles that the rest of the page might have set */
+  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+  border-width: 0;
+  background: transparent;
+  font-family: inherit;
+  font-size: inherit;
+  margin: 0;
+  white-space: pre;
+  word-wrap: normal;
+  line-height: inherit;
+  color: inherit;
+  z-index: 2;
+  position: relative;
+  overflow: visible;
+}
+.CodeMirror-wrap pre {
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  word-break: normal;
+}
+.CodeMirror-linebackground {
+  position: absolute;
+  left: 0; right: 0; top: 0; bottom: 0;
+  z-index: 0;
+}
+
+.CodeMirror-linewidget {
+  position: relative;
+  z-index: 2;
+  overflow: auto;
+}
+
+.CodeMirror-widget {
+  display: inline-block;
+}
+
+.CodeMirror-wrap .CodeMirror-scroll {
+  overflow-x: hidden;
+}
+
+.CodeMirror-measure {
+  position: absolute;
+  width: 100%; height: 0px;
+  overflow: hidden;
+  visibility: hidden;
+}
+.CodeMirror-measure pre { position: static; }
+
+.CodeMirror div.CodeMirror-cursor {
+  position: absolute;
+  visibility: hidden;
+  border-right: none;
+  width: 0;
+}
+.CodeMirror-focused div.CodeMirror-cursor {
+  visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+
+.cm-searching {
+  background: #ffa;
+  background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+@media print {
+  /* Hide the cursor when printing */
+  .CodeMirror div.CodeMirror-cursor {
+    visibility: hidden;
+  }
+}
diff --git a/Source/devtools/front_end/cm/codemirror.js b/Source/devtools/front_end/cm/codemirror.js
new file mode 100644
index 0000000..1570e49
--- /dev/null
+++ b/Source/devtools/front_end/cm/codemirror.js
@@ -0,0 +1,5585 @@
+// CodeMirror version 3.12
+//
+// CodeMirror is the only global var we claim
+window.CodeMirror = (function() {
+  "use strict";
+
+  // BROWSER SNIFFING
+
+  // Crude, but necessary to handle a number of hard-to-feature-detect
+  // bugs and behavior differences.
+  var gecko = /gecko\/\d/i.test(navigator.userAgent);
+  var ie = /MSIE \d/.test(navigator.userAgent);
+  var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
+  var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
+  var webkit = /WebKit\//.test(navigator.userAgent);
+  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
+  var chrome = /Chrome\//.test(navigator.userAgent);
+  var opera = /Opera\//.test(navigator.userAgent);
+  var safari = /Apple Computer/.test(navigator.vendor);
+  var khtml = /KHTML\//.test(navigator.userAgent);
+  var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
+  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
+  var phantom = /PhantomJS/.test(navigator.userAgent);
+
+  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
+  // This is woefully incomplete. Suggestions for alternative methods welcome.
+  var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
+  var mac = ios || /Mac/.test(navigator.platform);
+  var windows = /windows/i.test(navigator.platform);
+
+  var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
+  if (opera_version) opera_version = Number(opera_version[1]);
+  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
+  var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
+  var captureMiddleClick = gecko || (ie && !ie_lt9);
+
+  // Optimize some code when these features are not used
+  var sawReadOnlySpans = false, sawCollapsedSpans = false;
+
+  // CONSTRUCTOR
+
+  function CodeMirror(place, options) {
+    if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
+
+    this.options = options = options || {};
+    // Determine effective options based on given values and defaults.
+    for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
+      options[opt] = defaults[opt];
+    setGuttersForLineNumbers(options);
+
+    var docStart = typeof options.value == "string" ? 0 : options.value.first;
+    var display = this.display = makeDisplay(place, docStart);
+    display.wrapper.CodeMirror = this;
+    updateGutters(this);
+    if (options.autofocus && !mobile) focusInput(this);
+
+    this.state = {keyMaps: [],
+                  overlays: [],
+                  modeGen: 0,
+                  overwrite: false, focused: false,
+                  suppressEdits: false, pasteIncoming: false,
+                  draggingText: false,
+                  highlight: new Delayed()};
+
+    themeChanged(this);
+    if (options.lineWrapping)
+      this.display.wrapper.className += " CodeMirror-wrap";
+
+    var doc = options.value;
+    if (typeof doc == "string") doc = new Doc(options.value, options.mode);
+    operation(this, attachDoc)(this, doc);
+
+    // Override magic textarea content restore that IE sometimes does
+    // on our hidden textarea on reload
+    if (ie) setTimeout(bind(resetInput, this, true), 20);
+
+    registerEventHandlers(this);
+    // IE throws unspecified error in certain cases, when
+    // trying to access activeElement before onload
+    var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
+    if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
+    else onBlur(this);
+
+    operation(this, function() {
+      for (var opt in optionHandlers)
+        if (optionHandlers.propertyIsEnumerable(opt))
+          optionHandlers[opt](this, options[opt], Init);
+      for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
+    })();
+  }
+
+  // DISPLAY CONSTRUCTOR
+
+  function makeDisplay(place, docStart) {
+    var d = {};
+
+    var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
+    if (webkit) input.style.width = "1000px";
+    else input.setAttribute("wrap", "off");
+    // if border: 0; -- iOS fails to open keyboard (issue #1287)
+    if (ios) input.style.border = "1px solid black";
+    input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
+
+    // Wraps and hides input textarea
+    d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
+    // The actual fake scrollbars.
+    d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
+    d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
+    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
+    // DIVs containing the selection and the actual code
+    d.lineDiv = elt("div");
+    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
+    // Blinky cursor, and element used to ensure cursor fits at the end of a line
+    d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
+    // Secondary cursor, shown when on a 'jump' in bi-directional text
+    d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
+    // Used to measure text size
+    d.measure = elt("div", null, "CodeMirror-measure");
+    // Wraps everything that needs to exist inside the vertically-padded coordinate system
+    d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
+                         null, "position: relative; outline: none");
+    // Moved around its parent to cover visible view
+    d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
+    // Set to the height of the text, causes scrolling
+    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
+    // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
+    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
+    // Will contain the gutters, if any
+    d.gutters = elt("div", null, "CodeMirror-gutters");
+    d.lineGutter = null;
+    // Helper element to properly size the gutter backgrounds
+    var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
+    // Provides scrolling
+    d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
+    d.scroller.setAttribute("tabIndex", "-1");
+    // The element in which the editor lives.
+    d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
+                            d.scrollbarFiller, d.scroller], "CodeMirror");
+    // Work around IE7 z-index bug
+    if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
+    if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
+
+    // Needed to hide big blue blinking cursor on Mobile Safari
+    if (ios) input.style.width = "0px";
+    if (!webkit) d.scroller.draggable = true;
+    // Needed to handle Tab key in KHTML
+    if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
+    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
+    else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
+
+    // Current visible range (may be bigger than the view window).
+    d.viewOffset = d.lastSizeC = 0;
+    d.showingFrom = d.showingTo = docStart;
+
+    // Used to only resize the line number gutter when necessary (when
+    // the amount of lines crosses a boundary that makes its width change)
+    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
+    // See readInput and resetInput
+    d.prevInput = "";
+    // Set to true when a non-horizontal-scrolling widget is added. As
+    // an optimization, widget aligning is skipped when d is false.
+    d.alignWidgets = false;
+    // Flag that indicates whether we currently expect input to appear
+    // (after some event like 'keypress' or 'input') and are polling
+    // intensively.
+    d.pollingFast = false;
+    // Self-resetting timeout for the poller
+    d.poll = new Delayed();
+
+    d.cachedCharWidth = d.cachedTextHeight = null;
+    d.measureLineCache = [];
+    d.measureLineCachePos = 0;
+
+    // Tracks when resetInput has punted to just putting a short
+    // string instead of the (large) selection.
+    d.inaccurateSelection = false;
+
+    // Tracks the maximum line length so that the horizontal scrollbar
+    // can be kept static when scrolling.
+    d.maxLine = null;
+    d.maxLineLength = 0;
+    d.maxLineChanged = false;
+
+    // Used for measuring wheel scrolling granularity
+    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
+
+    return d;
+  }
+
+  // STATE UPDATES
+
+  // Used to get the editor into a consistent state again when options change.
+
+  function loadMode(cm) {
+    cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
+    cm.doc.iter(function(line) {
+      if (line.stateAfter) line.stateAfter = null;
+      if (line.styles) line.styles = null;
+    });
+    cm.doc.frontier = cm.doc.first;
+    startWorker(cm, 100);
+    cm.state.modeGen++;
+    if (cm.curOp) regChange(cm);
+  }
+
+  function wrappingChanged(cm) {
+    if (cm.options.lineWrapping) {
+      cm.display.wrapper.className += " CodeMirror-wrap";
+      cm.display.sizer.style.minWidth = "";
+    } else {
+      cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
+      computeMaxLength(cm);
+    }
+    estimateLineHeights(cm);
+    regChange(cm);
+    clearCaches(cm);
+    setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100);
+  }
+
+  function estimateHeight(cm) {
+    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
+    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
+    return function(line) {
+      if (lineIsHidden(cm.doc, line))
+        return 0;
+      else if (wrapping)
+        return (Math.ceil(line.text.length / perLine) || 1) * th;
+      else
+        return th;
+    };
+  }
+
+  function estimateLineHeights(cm) {
+    var doc = cm.doc, est = estimateHeight(cm);
+    doc.iter(function(line) {
+      var estHeight = est(line);
+      if (estHeight != line.height) updateLineHeight(line, estHeight);
+    });
+  }
+
+  function keyMapChanged(cm) {
+    var style = keyMap[cm.options.keyMap].style;
+    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
+      (style ? " cm-keymap-" + style : "");
+  }
+
+  function themeChanged(cm) {
+    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
+      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
+    clearCaches(cm);
+  }
+
+  function guttersChanged(cm) {
+    updateGutters(cm);
+    regChange(cm);
+  }
+
+  function updateGutters(cm) {
+    var gutters = cm.display.gutters, specs = cm.options.gutters;
+    removeChildren(gutters);
+    for (var i = 0; i < specs.length; ++i) {
+      var gutterClass = specs[i];
+      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
+      if (gutterClass == "CodeMirror-linenumbers") {
+        cm.display.lineGutter = gElt;
+        gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
+      }
+    }
+    gutters.style.display = i ? "" : "none";
+  }
+
+  function lineLength(doc, line) {
+    if (line.height == 0) return 0;
+    var len = line.text.length, merged, cur = line;
+    while (merged = collapsedSpanAtStart(cur)) {
+      var found = merged.find();
+      cur = getLine(doc, found.from.line);
+      len += found.from.ch - found.to.ch;
+    }
+    cur = line;
+    while (merged = collapsedSpanAtEnd(cur)) {
+      var found = merged.find();
+      len -= cur.text.length - found.from.ch;
+      cur = getLine(doc, found.to.line);
+      len += cur.text.length - found.to.ch;
+    }
+    return len;
+  }
+
+  function computeMaxLength(cm) {
+    var d = cm.display, doc = cm.doc;
+    d.maxLine = getLine(doc, doc.first);
+    d.maxLineLength = lineLength(doc, d.maxLine);
+    d.maxLineChanged = true;
+    doc.iter(function(line) {
+      var len = lineLength(doc, line);
+      if (len > d.maxLineLength) {
+        d.maxLineLength = len;
+        d.maxLine = line;
+      }
+    });
+  }
+
+  // Make sure the gutters options contains the element
+  // "CodeMirror-linenumbers" when the lineNumbers option is true.
+  function setGuttersForLineNumbers(options) {
+    var found = false;
+    for (var i = 0; i < options.gutters.length; ++i) {
+      if (options.gutters[i] == "CodeMirror-linenumbers") {
+        if (options.lineNumbers) found = true;
+        else options.gutters.splice(i--, 1);
+      }
+    }
+    if (!found && options.lineNumbers)
+      options.gutters.push("CodeMirror-linenumbers");
+  }
+
+  // SCROLLBARS
+
+  // Re-synchronize the fake scrollbars with the actual size of the
+  // content. Optionally force a scrollTop.
+  function updateScrollbars(d /* display */, docHeight) {
+    var totalHeight = docHeight + paddingVert(d);
+    d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
+    var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
+    var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
+    var needsV = scrollHeight > d.scroller.clientHeight;
+    if (needsV) {
+      d.scrollbarV.style.display = "block";
+      d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
+      d.scrollbarV.firstChild.style.height =
+        (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
+    } else d.scrollbarV.style.display = "";
+    if (needsH) {
+      d.scrollbarH.style.display = "block";
+      d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
+      d.scrollbarH.firstChild.style.width =
+        (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
+    } else d.scrollbarH.style.display = "";
+    if (needsH && needsV) {
+      d.scrollbarFiller.style.display = "block";
+      d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
+    } else d.scrollbarFiller.style.display = "";
+
+    if (mac_geLion && scrollbarWidth(d.measure) === 0)
+      d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
+  }
+
+  function visibleLines(display, doc, viewPort) {
+    var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
+    if (typeof viewPort == "number") top = viewPort;
+    else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
+    top = Math.floor(top - paddingTop(display));
+    var bottom = Math.ceil(top + height);
+    return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
+  }
+
+  // LINE NUMBERS
+
+  function alignHorizontally(cm) {
+    var display = cm.display;
+    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
+    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
+    var gutterW = display.gutters.offsetWidth, l = comp + "px";
+    for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
+      for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
+    }
+    if (cm.options.fixedGutter)
+      display.gutters.style.left = (comp + gutterW) + "px";
+  }
+
+  function maybeUpdateLineNumberWidth(cm) {
+    if (!cm.options.lineNumbers) return false;
+    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
+    if (last.length != display.lineNumChars) {
+      var test = display.measure.appendChild(elt("div", [elt("div", last)],
+                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
+      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
+      display.lineGutter.style.width = "";
+      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
+      display.lineNumWidth = display.lineNumInnerWidth + padding;
+      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
+      display.lineGutter.style.width = display.lineNumWidth + "px";
+      return true;
+    }
+    return false;
+  }
+
+  function lineNumberFor(options, i) {
+    return String(options.lineNumberFormatter(i + options.firstLineNumber));
+  }
+  function compensateForHScroll(display) {
+    return getRect(display.scroller).left - getRect(display.sizer).left;
+  }
+
+  // DISPLAY DRAWING
+
+  function updateDisplay(cm, changes, viewPort) {
+    var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
+    var visible = visibleLines(cm.display, cm.doc, viewPort);
+    for (;;) {
+      if (updateDisplayInner(cm, changes, visible)) {
+        updated = true;
+        signalLater(cm, "update", cm);
+        if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
+          signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
+      } else break;
+      updateSelection(cm);
+      updateScrollbars(cm.display, cm.doc.height);
+
+      // Clip forced viewport to actual scrollable area
+      if (viewPort)
+        viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
+                            typeof viewPort == "number" ? viewPort : viewPort.top);
+      visible = visibleLines(cm.display, cm.doc, viewPort);
+      if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
+        break;
+      changes = [];
+    }
+
+    return updated;
+  }
+
+  // Uses a set of changes plus the current scroll position to
+  // determine which DOM updates have to be made, and makes the
+  // updates.
+  function updateDisplayInner(cm, changes, visible) {
+    var display = cm.display, doc = cm.doc;
+    if (!display.wrapper.clientWidth) {
+      display.showingFrom = display.showingTo = doc.first;
+      display.viewOffset = 0;
+      return;
+    }
+
+    // Bail out if the visible area is already rendered and nothing changed.
+    if (changes.length == 0 &&
+        visible.from > display.showingFrom && visible.to < display.showingTo)
+      return;
+
+    if (maybeUpdateLineNumberWidth(cm))
+      changes = [{from: doc.first, to: doc.first + doc.size}];
+    var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
+    display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
+
+    // Used to determine which lines need their line numbers updated
+    var positionsChangedFrom = Infinity;
+    if (cm.options.lineNumbers)
+      for (var i = 0; i < changes.length; ++i)
+        if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
+
+    var end = doc.first + doc.size;
+    var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
+    var to = Math.min(end, visible.to + cm.options.viewportMargin);
+    if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
+    if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
+    if (sawCollapsedSpans) {
+      from = lineNo(visualLine(doc, getLine(doc, from)));
+      while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
+    }
+
+    // Create a range of theoretically intact lines, and punch holes
+    // in that using the change info.
+    var intact = [{from: Math.max(display.showingFrom, doc.first),
+                   to: Math.min(display.showingTo, end)}];
+    if (intact[0].from >= intact[0].to) intact = [];
+    else intact = computeIntact(intact, changes);
+    // When merged lines are present, we might have to reduce the
+    // intact ranges because changes in continued fragments of the
+    // intact lines do require the lines to be redrawn.
+    if (sawCollapsedSpans)
+      for (var i = 0; i < intact.length; ++i) {
+        var range = intact[i], merged;
+        while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
+          var newTo = merged.find().from.line;
+          if (newTo > range.from) range.to = newTo;
+          else { intact.splice(i--, 1); break; }
+        }
+      }
+
+    // Clip off the parts that won't be visible
+    var intactLines = 0;
+    for (var i = 0; i < intact.length; ++i) {
+      var range = intact[i];
+      if (range.from < from) range.from = from;
+      if (range.to > to) range.to = to;
+      if (range.from >= range.to) intact.splice(i--, 1);
+      else intactLines += range.to - range.from;
+    }
+    if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
+      updateViewOffset(cm);
+      return;
+    }
+    intact.sort(function(a, b) {return a.from - b.from;});
+
+    // Avoid crashing on IE's "unspecified error" when in iframes
+    try {
+      var focused = document.activeElement;
+    } catch(e) {}
+    if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
+    patchDisplay(cm, from, to, intact, positionsChangedFrom);
+    display.lineDiv.style.display = "";
+    if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus();
+
+    var different = from != display.showingFrom || to != display.showingTo ||
+      display.lastSizeC != display.wrapper.clientHeight;
+    // This is just a bogus formula that detects when the editor is
+    // resized or the font size changes.
+    if (different) display.lastSizeC = display.wrapper.clientHeight;
+    display.showingFrom = from; display.showingTo = to;
+    startWorker(cm, 100);
+
+    var prevBottom = display.lineDiv.offsetTop;
+    for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
+      if (ie_lt8) {
+        var bot = node.offsetTop + node.offsetHeight;
+        height = bot - prevBottom;
+        prevBottom = bot;
+      } else {
+        var box = getRect(node);
+        height = box.bottom - box.top;
+      }
+      var diff = node.lineObj.height - height;
+      if (height < 2) height = textHeight(display);
+      if (diff > .001 || diff < -.001) {
+        updateLineHeight(node.lineObj, height);
+        var widgets = node.lineObj.widgets;
+        if (widgets) for (var i = 0; i < widgets.length; ++i)
+          widgets[i].height = widgets[i].node.offsetHeight;
+      }
+    }
+    updateViewOffset(cm);
+
+    return true;
+  }
+
+  function updateViewOffset(cm) {
+    var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
+    // Position the mover div to align with the current virtual scroll position
+    cm.display.mover.style.top = off + "px";
+  }
+
+  function computeIntact(intact, changes) {
+    for (var i = 0, l = changes.length || 0; i < l; ++i) {
+      var change = changes[i], intact2 = [], diff = change.diff || 0;
+      for (var j = 0, l2 = intact.length; j < l2; ++j) {
+        var range = intact[j];
+        if (change.to <= range.from && change.diff) {
+          intact2.push({from: range.from + diff, to: range.to + diff});
+        } else if (change.to <= range.from || change.from >= range.to) {
+          intact2.push(range);
+        } else {
+          if (change.from > range.from)
+            intact2.push({from: range.from, to: change.from});
+          if (change.to < range.to)
+            intact2.push({from: change.to + diff, to: range.to + diff});
+        }
+      }
+      intact = intact2;
+    }
+    return intact;
+  }
+
+  function getDimensions(cm) {
+    var d = cm.display, left = {}, width = {};
+    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
+      left[cm.options.gutters[i]] = n.offsetLeft;
+      width[cm.options.gutters[i]] = n.offsetWidth;
+    }
+    return {fixedPos: compensateForHScroll(d),
+            gutterTotalWidth: d.gutters.offsetWidth,
+            gutterLeft: left,
+            gutterWidth: width,
+            wrapperWidth: d.wrapper.clientWidth};
+  }
+
+  function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
+    var dims = getDimensions(cm);
+    var display = cm.display, lineNumbers = cm.options.lineNumbers;
+    if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
+      removeChildren(display.lineDiv);
+    var container = display.lineDiv, cur = container.firstChild;
+
+    function rm(node) {
+      var next = node.nextSibling;
+      if (webkit && mac && cm.display.currentWheelTarget == node) {
+        node.style.display = "none";
+        node.lineObj = null;
+      } else {
+        node.parentNode.removeChild(node);
+      }
+      return next;
+    }
+
+    var nextIntact = intact.shift(), lineN = from;
+    cm.doc.iter(from, to, function(line) {
+      if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
+      if (lineIsHidden(cm.doc, line)) {
+        if (line.height != 0) updateLineHeight(line, 0);
+        if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
+          if (line.widgets[i].showIfHidden) {
+            var prev = cur.previousSibling;
+            if (/pre/i.test(prev.nodeName)) {
+              var wrap = elt("div", null, null, "position: relative");
+              prev.parentNode.replaceChild(wrap, prev);
+              wrap.appendChild(prev);
+              prev = wrap;
+            }
+            var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
+            positionLineWidget(line.widgets[i], wnode, prev, dims);
+          }
+      } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
+        // This line is intact. Skip to the actual node. Update its
+        // line number if needed.
+        while (cur.lineObj != line) cur = rm(cur);
+        if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
+          setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
+        cur = cur.nextSibling;
+      } else {
+        // For lines with widgets, make an attempt to find and reuse
+        // the existing element, so that widgets aren't needlessly
+        // removed and re-inserted into the dom
+        if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
+          if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
+        // This line needs to be generated.
+        var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
+        if (lineNode != reuse) {
+          container.insertBefore(lineNode, cur);
+        } else {
+          while (cur != reuse) cur = rm(cur);
+          cur = cur.nextSibling;
+        }
+
+        lineNode.lineObj = line;
+      }
+      ++lineN;
+    });
+    while (cur) cur = rm(cur);
+  }
+
+  function buildLineElement(cm, line, lineNo, dims, reuse) {
+    var lineElement = lineContent(cm, line);
+    var markers = line.gutterMarkers, display = cm.display, wrap;
+
+    if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
+      return lineElement;
+
+    // Lines with gutter elements, widgets or a background class need
+    // to be wrapped again, and have the extra elements added to the
+    // wrapper div
+
+    if (reuse) {
+      reuse.alignable = null;
+      var isOk = true, widgetsSeen = 0;
+      for (var n = reuse.firstChild, next; n; n = next) {
+        next = n.nextSibling;
+        if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
+          reuse.removeChild(n);
+        } else {
+          for (var i = 0, first = true; i < line.widgets.length; ++i) {
+            var widget = line.widgets[i], isFirst = false;
+            if (!widget.above) { isFirst = first; first = false; }
+            if (widget.node == n.firstChild) {
+              positionLineWidget(widget, n, reuse, dims);
+              ++widgetsSeen;
+              if (isFirst) reuse.insertBefore(lineElement, n);
+              break;
+            }
+          }
+          if (i == line.widgets.length) { isOk = false; break; }
+        }
+      }
+      if (isOk && widgetsSeen == line.widgets.length) {
+        wrap = reuse;
+        reuse.className = line.wrapClass || "";
+      }
+    }
+    if (!wrap) {
+      wrap = elt("div", null, line.wrapClass, "position: relative");
+      wrap.appendChild(lineElement);
+    }
+    // Kludge to make sure the styled element lies behind the selection (by z-index)
+    if (line.bgClass)
+      wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
+    if (cm.options.lineNumbers || markers) {
+      var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
+                                             (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
+                                         wrap.firstChild);
+      if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
+      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
+        wrap.lineNumber = gutterWrap.appendChild(
+          elt("div", lineNumberFor(cm.options, lineNo),
+              "CodeMirror-linenumber CodeMirror-gutter-elt",
+              "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
+              + display.lineNumInnerWidth + "px"));
+      if (markers)
+        for (var k = 0; k < cm.options.gutters.length; ++k) {
+          var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
+          if (found)
+            gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
+                                       dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
+        }
+    }
+    if (ie_lt8) wrap.style.zIndex = 2;
+    if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
+      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
+      positionLineWidget(widget, node, wrap, dims);
+      if (widget.above)
+        wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
+      else
+        wrap.appendChild(node);
+      signalLater(widget, "redraw");
+    }
+    return wrap;
+  }
+
+  function positionLineWidget(widget, node, wrap, dims) {
+    if (widget.noHScroll) {
+      (wrap.alignable || (wrap.alignable = [])).push(node);
+      var width = dims.wrapperWidth;
+      node.style.left = dims.fixedPos + "px";
+      if (!widget.coverGutter) {
+        width -= dims.gutterTotalWidth;
+        node.style.paddingLeft = dims.gutterTotalWidth + "px";
+      }
+      node.style.width = width + "px";
+    }
+    if (widget.coverGutter) {
+      node.style.zIndex = 5;
+      node.style.position = "relative";
+      if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
+    }
+  }
+
+  // SELECTION / CURSOR
+
+  function updateSelection(cm) {
+    var display = cm.display;
+    var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
+    if (collapsed || cm.options.showCursorWhenSelecting)
+      updateSelectionCursor(cm);
+    else
+      display.cursor.style.display = display.otherCursor.style.display = "none";
+    if (!collapsed)
+      updateSelectionRange(cm);
+    else
+      display.selectionDiv.style.display = "none";
+
+    // Move the hidden textarea near the cursor to prevent scrolling artifacts
+    if (cm.options.moveInputWithCursor) {
+      var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
+      var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
+      display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
+                                                        headPos.top + lineOff.top - wrapOff.top)) + "px";
+      display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
+                                                         headPos.left + lineOff.left - wrapOff.left)) + "px";
+    }
+  }
+
+  // No selection, plain cursor
+  function updateSelectionCursor(cm) {
+    var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
+    display.cursor.style.left = pos.left + "px";
+    display.cursor.style.top = pos.top + "px";
+    display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
+    display.cursor.style.display = "";
+
+    if (pos.other) {
+      display.otherCursor.style.display = "";
+      display.otherCursor.style.left = pos.other.left + "px";
+      display.otherCursor.style.top = pos.other.top + "px";
+      display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
+    } else { display.otherCursor.style.display = "none"; }
+  }
+
+  // Highlight selection
+  function updateSelectionRange(cm) {
+    var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
+    var fragment = document.createDocumentFragment();
+    var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
+
+    function add(left, top, width, bottom) {
+      if (top < 0) top = 0;
+      fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
+                               "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
+                               "px; height: " + (bottom - top) + "px"));
+    }
+
+    function drawForLine(line, fromArg, toArg, retTop) {
+      var lineObj = getLine(doc, line);
+      var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
+      function coords(ch) {
+        return charCoords(cm, Pos(line, ch), "div", lineObj);
+      }
+
+      iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
+        var leftPos = coords(from), rightPos, left, right;
+        if (from == to) {
+          rightPos = leftPos;
+          left = right = leftPos.left;
+        } else {
+          rightPos = coords(to - 1);
+          if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
+          left = leftPos.left;
+          right = rightPos.right;
+        }
+        if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
+          add(left, leftPos.top, null, leftPos.bottom);
+          left = pl;
+          if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
+        }
+        if (toArg == null && to == lineLen) right = clientWidth;
+        if (fromArg == null && from == 0) left = pl;
+        rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
+        if (left < pl + 1) left = pl;
+        add(left, rightPos.top, right - left, rightPos.bottom);
+      });
+      return rVal;
+    }
+
+    if (sel.from.line == sel.to.line) {
+      drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
+    } else {
+      var fromObj = getLine(doc, sel.from.line);
+      var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
+      while (merged = collapsedSpanAtEnd(cur)) {
+        var found = merged.find();
+        path.push(found.from.ch, found.to.line, found.to.ch);
+        if (found.to.line == sel.to.line) {
+          path.push(sel.to.ch);
+          singleLine = true;
+          break;
+        }
+        cur = getLine(doc, found.to.line);
+      }
+
+      // This is a single, merged line
+      if (singleLine) {
+        for (var i = 0; i < path.length; i += 3)
+          drawForLine(path[i], path[i+1], path[i+2]);
+      } else {
+        var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
+        if (sel.from.ch)
+          // Draw the first line of selection.
+          middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
+        else
+          // Simply include it in the middle block.
+          middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
+
+        if (!sel.to.ch)
+          middleBot = heightAtLine(cm, toObj) - display.viewOffset;
+        else
+          middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
+
+        if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
+      }
+    }
+
+    removeChildrenAndAdd(display.selectionDiv, fragment);
+    display.selectionDiv.style.display = "";
+  }
+
+  // Cursor-blinking
+  function restartBlink(cm) {
+    if (!cm.state.focused) return;
+    var display = cm.display;
+    clearInterval(display.blinker);
+    var on = true;
+    display.cursor.style.visibility = display.otherCursor.style.visibility = "";
+    display.blinker = setInterval(function() {
+      display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
+    }, cm.options.cursorBlinkRate);
+  }
+
+  // HIGHLIGHT WORKER
+
+  function startWorker(cm, time) {
+    if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
+      cm.state.highlight.set(time, bind(highlightWorker, cm));
+  }
+
+  function highlightWorker(cm) {
+    var doc = cm.doc;
+    if (doc.frontier < doc.first) doc.frontier = doc.first;
+    if (doc.frontier >= cm.display.showingTo) return;
+    var end = +new Date + cm.options.workTime;
+    var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
+    var changed = [], prevChange;
+    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
+      if (doc.frontier >= cm.display.showingFrom) { // Visible
+        var oldStyles = line.styles;
+        line.styles = highlightLine(cm, line, state);
+        var ischange = !oldStyles || oldStyles.length != line.styles.length;
+        for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
+        if (ischange) {
+          if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
+          else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
+        }
+        line.stateAfter = copyState(doc.mode, state);
+      } else {
+        processLine(cm, line, state);
+        line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
+      }
+      ++doc.frontier;
+      if (+new Date > end) {
+        startWorker(cm, cm.options.workDelay);
+        return true;
+      }
+    });
+    if (changed.length)
+      operation(cm, function() {
+        for (var i = 0; i < changed.length; ++i)
+          regChange(this, changed[i].start, changed[i].end);
+      })();
+  }
+
+  // Finds the line to start with when starting a parse. Tries to
+  // find a line with a stateAfter, so that it can start with a
+  // valid state. If that fails, it returns the line with the
+  // smallest indentation, which tends to need the least context to
+  // parse correctly.
+  function findStartLine(cm, n) {
+    var minindent, minline, doc = cm.doc;
+    for (var search = n, lim = n - 100; search > lim; --search) {
+      if (search <= doc.first) return doc.first;
+      var line = getLine(doc, search - 1);
+      if (line.stateAfter) return search;
+      var indented = countColumn(line.text, null, cm.options.tabSize);
+      if (minline == null || minindent > indented) {
+        minline = search - 1;
+        minindent = indented;
+      }
+    }
+    return minline;
+  }
+
+  function getStateBefore(cm, n) {
+    var doc = cm.doc, display = cm.display;
+      if (!doc.mode.startState) return true;
+    var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
+    if (!state) state = startState(doc.mode);
+    else state = copyState(doc.mode, state);
+    doc.iter(pos, n, function(line) {
+      processLine(cm, line, state);
+      var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
+      line.stateAfter = save ? copyState(doc.mode, state) : null;
+      ++pos;
+    });
+    return state;
+  }
+
+  // POSITION MEASUREMENT
+
+  function paddingTop(display) {return display.lineSpace.offsetTop;}
+  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
+  function paddingLeft(display) {
+    var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
+    return e.offsetLeft;
+  }
+
+  function measureChar(cm, line, ch, data) {
+    var dir = -1;
+    data = data || measureLine(cm, line);
+
+    for (var pos = ch;; pos += dir) {
+      var r = data[pos];
+      if (r) break;
+      if (dir < 0 && pos == 0) dir = 1;
+    }
+    return {left: pos < ch ? r.right : r.left,
+            right: pos > ch ? r.left : r.right,
+            top: r.top, bottom: r.bottom};
+  }
+
+  function findCachedMeasurement(cm, line) {
+    var cache = cm.display.measureLineCache;
+    for (var i = 0; i < cache.length; ++i) {
+      var memo = cache[i];
+      if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
+          cm.display.scroller.clientWidth == memo.width &&
+          memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
+        return memo.measure;
+    }
+  }
+
+  function measureLine(cm, line) {
+    // First look in the cache
+    var measure = findCachedMeasurement(cm, line);
+    if (!measure) {
+      // Failing that, recompute and store result in cache
+      measure = measureLineInner(cm, line);
+      var cache = cm.display.measureLineCache;
+      var memo = {text: line.text, width: cm.display.scroller.clientWidth,
+                  markedSpans: line.markedSpans, measure: measure,
+                  classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
+      if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
+      else cache.push(memo);
+    }
+    return measure;
+  }
+
+  function measureLineInner(cm, line) {
+    var display = cm.display, measure = emptyArray(line.text.length);
+    var pre = lineContent(cm, line, measure);
+
+    // IE does not cache element positions of inline elements between
+    // calls to getBoundingClientRect. This makes the loop below,
+    // which gathers the positions of all the characters on the line,
+    // do an amount of layout work quadratic to the number of
+    // characters. When line wrapping is off, we try to improve things
+    // by first subdividing the line into a bunch of inline blocks, so
+    // that IE can reuse most of the layout information from caches
+    // for those blocks. This does interfere with line wrapping, so it
+    // doesn't work when wrapping is on, but in that case the
+    // situation is slightly better, since IE does cache line-wrapping
+    // information and only recomputes per-line.
+    if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
+      var fragment = document.createDocumentFragment();
+      var chunk = 10, n = pre.childNodes.length;
+      for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
+        var wrap = elt("div", null, null, "display: inline-block");
+        for (var j = 0; j < chunk && n; ++j) {
+          wrap.appendChild(pre.firstChild);
+          --n;
+        }
+        fragment.appendChild(wrap);
+      }
+      pre.appendChild(fragment);
+    }
+
+    removeChildrenAndAdd(display.measure, pre);
+
+    var outer = getRect(display.lineDiv);
+    var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
+    // Work around an IE7/8 bug where it will sometimes have randomly
+    // replaced our pre with a clone at this point.
+    if (ie_lt9 && display.measure.first != pre)
+      removeChildrenAndAdd(display.measure, pre);
+
+    for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
+      var size = getRect(cur);
+      var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
+      for (var j = 0; j < vranges.length; j += 2) {
+        var rtop = vranges[j], rbot = vranges[j+1];
+        if (rtop > bot || rbot < top) continue;
+        if (rtop <= top && rbot >= bot ||
+            top <= rtop && bot >= rbot ||
+            Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
+          vranges[j] = Math.min(top, rtop);
+          vranges[j+1] = Math.max(bot, rbot);
+          break;
+        }
+      }
+      if (j == vranges.length) vranges.push(top, bot);
+      var right = size.right;
+      if (cur.measureRight) right = getRect(cur.measureRight).left;
+      data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
+    }
+    for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
+      var vr = cur.top;
+      cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
+    }
+
+    return data;
+  }
+
+  function measureLineWidth(cm, line) {
+    var hasBadSpan = false;
+    if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
+      var sp = line.markedSpans[i];
+      if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
+    }
+    var cached = !hasBadSpan && findCachedMeasurement(cm, line);
+    if (cached) return measureChar(cm, line, line.text.length, cached).right;
+
+    var pre = lineContent(cm, line);
+    var end = pre.appendChild(zeroWidthElement(cm.display.measure));
+    removeChildrenAndAdd(cm.display.measure, pre);
+    return getRect(end).right - getRect(cm.display.lineDiv).left;
+  }
+
+  function clearCaches(cm) {
+    cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
+    cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
+    if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
+    cm.display.lineNumChars = null;
+  }
+
+  // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
+  function intoCoordSystem(cm, lineObj, rect, context) {
+    if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
+      var size = widgetHeight(lineObj.widgets[i]);
+      rect.top += size; rect.bottom += size;
+    }
+    if (context == "line") return rect;
+    if (!context) context = "local";
+    var yOff = heightAtLine(cm, lineObj);
+    if (context != "local") yOff -= cm.display.viewOffset;
+    if (context == "page") {
+      var lOff = getRect(cm.display.lineSpace);
+      yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
+      var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
+      rect.left += xOff; rect.right += xOff;
+    }
+    rect.top += yOff; rect.bottom += yOff;
+    return rect;
+  }
+
+  // Context may be "window", "page", "div", or "local"/null
+  // Result is in "div" coords
+  function fromCoordSystem(cm, coords, context) {
+    if (context == "div") return coords;
+    var left = coords.left, top = coords.top;
+    if (context == "page") {
+      left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
+      top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
+    }
+    var lineSpaceBox = getRect(cm.display.lineSpace);
+    left -= lineSpaceBox.left;
+    top -= lineSpaceBox.top;
+    if (context == "local" || !context) {
+      var editorBox = getRect(cm.display.wrapper);
+      left += editorBox.left;
+      top += editorBox.top;
+    }
+    return {left: left, top: top};
+  }
+
+  function charCoords(cm, pos, context, lineObj) {
+    if (!lineObj) lineObj = getLine(cm.doc, pos.line);
+    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
+  }
+
+  function cursorCoords(cm, pos, context, lineObj, measurement) {
+    lineObj = lineObj || getLine(cm.doc, pos.line);
+    if (!measurement) measurement = measureLine(cm, lineObj);
+    function get(ch, right) {
+      var m = measureChar(cm, lineObj, ch, measurement);
+      if (right) m.left = m.right; else m.right = m.left;
+      return intoCoordSystem(cm, lineObj, m, context);
+    }
+    var order = getOrder(lineObj), ch = pos.ch;
+    if (!order) return get(ch);
+    var main, other, linedir = order[0].level;
+    for (var i = 0; i < order.length; ++i) {
+      var part = order[i], rtl = part.level % 2, nb, here;
+      if (part.from < ch && part.to > ch) return get(ch, rtl);
+      var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
+      if (left == ch) {
+        // IE returns bogus offsets and widths for edges where the
+        // direction flips, but only for the side with the lower
+        // level. So we try to use the side with the higher level.
+        if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
+        else here = get(rtl && part.from != part.to ? ch - 1 : ch);
+        if (rtl == linedir) main = here; else other = here;
+      } else if (right == ch) {
+        var nb = i < order.length - 1 && order[i+1];
+        if (!rtl && nb && nb.from == nb.to) continue;
+        if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
+        else here = get(rtl ? ch : ch - 1, true);
+        if (rtl == linedir) main = here; else other = here;
+      }
+    }
+    if (linedir && !ch) other = get(order[0].to - 1);
+    if (!main) return other;
+    if (other) main.other = other;
+    return main;
+  }
+
+  function PosMaybeOutside(line, ch, outside) {
+    var pos = new Pos(line, ch);
+    if (outside) pos.outside = true;
+    return pos;
+  }
+
+  // Coords must be lineSpace-local
+  function coordsChar(cm, x, y) {
+    var doc = cm.doc;
+    y += cm.display.viewOffset;
+    if (y < 0) return PosMaybeOutside(doc.first, 0, true);
+    var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
+    if (lineNo > last)
+      return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
+    if (x < 0) x = 0;
+
+    for (;;) {
+      var lineObj = getLine(doc, lineNo);
+      var found = coordsCharInner(cm, lineObj, lineNo, x, y);
+      var merged = collapsedSpanAtEnd(lineObj);
+      var mergedPos = merged && merged.find();
+      if (merged && found.ch >= mergedPos.from.ch)
+        lineNo = mergedPos.to.line;
+      else
+        return found;
+    }
+  }
+
+  function coordsCharInner(cm, lineObj, lineNo, x, y) {
+    var innerOff = y - heightAtLine(cm, lineObj);
+    var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
+    var measurement = measureLine(cm, lineObj);
+
+    function getX(ch) {
+      var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
+                            lineObj, measurement);
+      wrongLine = true;
+      if (innerOff > sp.bottom) return sp.left - adjust;
+      else if (innerOff < sp.top) return sp.left + adjust;
+      else wrongLine = false;
+      return sp.left;
+    }
+
+    var bidi = getOrder(lineObj), dist = lineObj.text.length;
+    var from = lineLeft(lineObj), to = lineRight(lineObj);
+    var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
+
+    if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
+    // Do a binary search between these bounds.
+    for (;;) {
+      if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
+        var after = x - fromX < toX - x, ch = after ? from : to;
+        while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
+        var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
+        pos.after = after;
+        return pos;
+      }
+      var step = Math.ceil(dist / 2), middle = from + step;
+      if (bidi) {
+        middle = from;
+        for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
+      }
+      var middleX = getX(middle);
+      if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
+      else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
+    }
+  }
+
+  var measureText;
+  function textHeight(display) {
+    if (display.cachedTextHeight != null) return display.cachedTextHeight;
+    if (measureText == null) {
+      measureText = elt("pre");
+      // Measure a bunch of lines, for browsers that compute
+      // fractional heights.
+      for (var i = 0; i < 49; ++i) {
+        measureText.appendChild(document.createTextNode("x"));
+        measureText.appendChild(elt("br"));
+      }
+      measureText.appendChild(document.createTextNode("x"));
+    }
+    removeChildrenAndAdd(display.measure, measureText);
+    var height = measureText.offsetHeight / 50;
+    if (height > 3) display.cachedTextHeight = height;
+    removeChildren(display.measure);
+    return height || 1;
+  }
+
+  function charWidth(display) {
+    if (display.cachedCharWidth != null) return display.cachedCharWidth;
+    var anchor = elt("span", "x");
+    var pre = elt("pre", [anchor]);
+    removeChildrenAndAdd(display.measure, pre);
+    var width = anchor.offsetWidth;
+    if (width > 2) display.cachedCharWidth = width;
+    return width || 10;
+  }
+
+  // OPERATIONS
+
+  // Operations are used to wrap changes in such a way that each
+  // change won't have to update the cursor and display (which would
+  // be awkward, slow, and error-prone), but instead updates are
+  // batched and then all combined and executed at once.
+
+  var nextOpId = 0;
+  function startOperation(cm) {
+    cm.curOp = {
+      // An array of ranges of lines that have to be updated. See
+      // updateDisplay.
+      changes: [],
+      updateInput: null,
+      userSelChange: null,
+      textChanged: null,
+      selectionChanged: false,
+      cursorActivity: false,
+      updateMaxLine: false,
+      updateScrollPos: false,
+      id: ++nextOpId
+    };
+    if (!delayedCallbackDepth++) delayedCallbacks = [];
+  }
+
+  function endOperation(cm) {
+    var op = cm.curOp, doc = cm.doc, display = cm.display;
+    cm.curOp = null;
+
+    if (op.updateMaxLine) computeMaxLength(cm);
+    if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
+      var width = measureLineWidth(cm, display.maxLine);
+      display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
+      display.maxLineChanged = false;
+      var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
+      if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
+        setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
+    }
+    var newScrollPos, updated;
+    if (op.updateScrollPos) {
+      newScrollPos = op.updateScrollPos;
+    } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
+      var coords = cursorCoords(cm, doc.sel.head);
+      newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
+    }
+    if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
+      updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
+      if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
+    }
+    if (!updated && op.selectionChanged) updateSelection(cm);
+    if (op.updateScrollPos) {
+      display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
+      display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
+      alignHorizontally(cm);
+      if (op.scrollToPos)
+        scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
+    } else if (newScrollPos) {
+      scrollCursorIntoView(cm);
+    }
+    if (op.selectionChanged) restartBlink(cm);
+
+    if (cm.state.focused && op.updateInput)
+      resetInput(cm, op.userSelChange);
+
+    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
+    if (hidden) for (var i = 0; i < hidden.length; ++i)
+      if (!hidden[i].lines.length) signal(hidden[i], "hide");
+    if (unhidden) for (var i = 0; i < unhidden.length; ++i)
+      if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
+
+    var delayed;
+    if (!--delayedCallbackDepth) {
+      delayed = delayedCallbacks;
+      delayedCallbacks = null;
+    }
+    if (op.textChanged)
+      signal(cm, "change", cm, op.textChanged);
+    if (op.cursorActivity) signal(cm, "cursorActivity", cm);
+    if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
+  }
+
+  // Wraps a function in an operation. Returns the wrapped function.
+  function operation(cm1, f) {
+    return function() {
+      var cm = cm1 || this, withOp = !cm.curOp;
+      if (withOp) startOperation(cm);
+      try { var result = f.apply(cm, arguments); }
+      finally { if (withOp) endOperation(cm); }
+      return result;
+    };
+  }
+  function docOperation(f) {
+    return function() {
+      var withOp = this.cm && !this.cm.curOp, result;
+      if (withOp) startOperation(this.cm);
+      try { result = f.apply(this, arguments); }
+      finally { if (withOp) endOperation(this.cm); }
+      return result;
+    };
+  }
+  function runInOp(cm, f) {
+    var withOp = !cm.curOp, result;
+    if (withOp) startOperation(cm);
+    try { result = f(); }
+    finally { if (withOp) endOperation(cm); }
+    return result;
+  }
+
+  function regChange(cm, from, to, lendiff) {
+    if (from == null) from = cm.doc.first;
+    if (to == null) to = cm.doc.first + cm.doc.size;
+    cm.curOp.changes.push({from: from, to: to, diff: lendiff});
+  }
+
+  // INPUT HANDLING
+
+  function slowPoll(cm) {
+    if (cm.display.pollingFast) return;
+    cm.display.poll.set(cm.options.pollInterval, function() {
+      readInput(cm);
+      if (cm.state.focused) slowPoll(cm);
+    });
+  }
+
+  function fastPoll(cm) {
+    var missed = false;
+    cm.display.pollingFast = true;
+    function p() {
+      var changed = readInput(cm);
+      if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
+      else {cm.display.pollingFast = false; slowPoll(cm);}
+    }
+    cm.display.poll.set(20, p);
+  }
+
+  // prevInput is a hack to work with IME. If we reset the textarea
+  // on every change, that breaks IME. So we look for changes
+  // compared to the previous content instead. (Modern browsers have
+  // events that indicate IME taking place, but these are not widely
+  // supported or compatible enough yet to rely on.)
+  function readInput(cm) {
+    var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
+    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
+    var text = input.value;
+    if (text == prevInput && posEq(sel.from, sel.to)) return false;
+    if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
+      resetInput(cm, true);
+      return false;
+    }
+
+    var withOp = !cm.curOp;
+    if (withOp) startOperation(cm);
+    sel.shift = false;
+    var same = 0, l = Math.min(prevInput.length, text.length);
+    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
+    var from = sel.from, to = sel.to;
+    if (same < prevInput.length)
+      from = Pos(from.line, from.ch - (prevInput.length - same));
+    else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
+      to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
+    var updateInput = cm.curOp.updateInput;
+    makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
+                        origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
+
+    cm.curOp.updateInput = updateInput;
+    if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
+    else cm.display.prevInput = text;
+    if (withOp) endOperation(cm);
+    cm.state.pasteIncoming = false;
+    return true;
+  }
+
+  function resetInput(cm, user) {
+    var minimal, selected, doc = cm.doc;
+    if (!posEq(doc.sel.from, doc.sel.to)) {
+      cm.display.prevInput = "";
+      minimal = hasCopyEvent &&
+        (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
+      var content = minimal ? "-" : selected || cm.getSelection();
+      cm.display.input.value = content;
+      if (cm.state.focused) selectInput(cm.display.input);
+      if (ie && !ie_lt9) cm.display.inputHasSelection = content;
+    } else if (user) {
+      cm.display.prevInput = cm.display.input.value = "";
+      if (ie && !ie_lt9) cm.display.inputHasSelection = null;
+    }
+    cm.display.inaccurateSelection = minimal;
+  }
+
+  function focusInput(cm) {
+    if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
+      cm.display.input.focus();
+  }
+
+  function isReadOnly(cm) {
+    return cm.options.readOnly || cm.doc.cantEdit;
+  }
+
+  // EVENT HANDLERS
+
+  function registerEventHandlers(cm) {
+    var d = cm.display;
+    on(d.scroller, "mousedown", operation(cm, onMouseDown));
+    if (ie)
+      on(d.scroller, "dblclick", operation(cm, function(e) {
+        var pos = posFromMouse(cm, e);
+        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
+        e_preventDefault(e);
+        var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
+        extendSelection(cm.doc, word.from, word.to);
+      }));
+    else
+      on(d.scroller, "dblclick", e_preventDefault);
+    on(d.lineSpace, "selectstart", function(e) {
+      if (!eventInWidget(d, e)) e_preventDefault(e);
+    });
+    // Gecko browsers fire contextmenu *after* opening the menu, at
+    // which point we can't mess with it anymore. Context menu is
+    // handled in onMouseDown for Gecko.
+    if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
+
+    on(d.scroller, "scroll", function() {
+      if (d.scroller.clientHeight) {
+        setScrollTop(cm, d.scroller.scrollTop);
+        setScrollLeft(cm, d.scroller.scrollLeft, true);
+        signal(cm, "scroll", cm);
+      }
+    });
+    on(d.scrollbarV, "scroll", function() {
+      if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
+    });
+    on(d.scrollbarH, "scroll", function() {
+      if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
+    });
+
+    on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
+    on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
+
+    function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
+    on(d.scrollbarH, "mousedown", reFocus);
+    on(d.scrollbarV, "mousedown", reFocus);
+    // Prevent wrapper from ever scrolling
+    on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
+
+    function onResize() {
+      // Might be a text scaling operation, clear size caches.
+      d.cachedCharWidth = d.cachedTextHeight = null;
+      clearCaches(cm);
+      runInOp(cm, bind(regChange, cm));
+    }
+    on(window, "resize", onResize);
+    // Above handler holds on to the editor and its data structures.
+    // Here we poll to unregister it when the editor is no longer in
+    // the document, so that it can be garbage-collected.
+    function unregister() {
+      for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
+      if (p) setTimeout(unregister, 5000);
+      else off(window, "resize", onResize);
+    }
+    setTimeout(unregister, 5000);
+
+    on(d.input, "keyup", operation(cm, function(e) {
+      if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+      if (e.keyCode == 16) cm.doc.sel.shift = false;
+    }));
+    on(d.input, "input", bind(fastPoll, cm));
+    on(d.input, "keydown", operation(cm, onKeyDown));
+    on(d.input, "keypress", operation(cm, onKeyPress));
+    on(d.input, "focus", bind(onFocus, cm));
+    on(d.input, "blur", bind(onBlur, cm));
+
+    function drag_(e) {
+      if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
+      e_stop(e);
+    }
+    if (cm.options.dragDrop) {
+      on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
+      on(d.scroller, "dragenter", drag_);
+      on(d.scroller, "dragover", drag_);
+      on(d.scroller, "drop", operation(cm, onDrop));
+    }
+    on(d.scroller, "paste", function(e){
+      if (eventInWidget(d, e)) return;
+      focusInput(cm);
+      fastPoll(cm);
+    });
+    on(d.input, "paste", function() {
+      cm.state.pasteIncoming = true;
+      fastPoll(cm);
+    });
+
+    function prepareCopy() {
+      if (d.inaccurateSelection) {
+        d.prevInput = "";
+        d.inaccurateSelection = false;
+        d.input.value = cm.getSelection();
+        selectInput(d.input);
+      }
+    }
+    on(d.input, "cut", prepareCopy);
+    on(d.input, "copy", prepareCopy);
+
+    // Needed to handle Tab key in KHTML
+    if (khtml) on(d.sizer, "mouseup", function() {
+        if (document.activeElement == d.input) d.input.blur();
+        focusInput(cm);
+    });
+  }
+
+  function eventInWidget(display, e) {
+    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
+      if (!n) return true;
+      if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
+          n.parentNode == display.sizer && n != display.mover) return true;
+    }
+  }
+
+  function posFromMouse(cm, e, liberal) {
+    var display = cm.display;
+    if (!liberal) {
+      var target = e_target(e);
+      if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
+          target == display.scrollbarV || target == display.scrollbarV.firstChild ||
+          target == display.scrollbarFiller) return null;
+    }
+    var x, y, space = getRect(display.lineSpace);
+    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
+    try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
+    return coordsChar(cm, x - space.left, y - space.top);
+  }
+
+  var lastClick, lastDoubleClick;
+  function onMouseDown(e) {
+    var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
+    sel.shift = e.shiftKey;
+
+    if (eventInWidget(display, e)) {
+      if (!webkit) {
+        display.scroller.draggable = false;
+        setTimeout(function(){display.scroller.draggable = true;}, 100);
+      }
+      return;
+    }
+    if (clickInGutter(cm, e)) return;
+    var start = posFromMouse(cm, e);
+
+    switch (e_button(e)) {
+    case 3:
+      if (captureMiddleClick) onContextMenu.call(cm, cm, e);
+      return;
+    case 2:
+      if (start) extendSelection(cm.doc, start);
+      setTimeout(bind(focusInput, cm), 20);
+      e_preventDefault(e);
+      return;
+    }
+    // For button 1, if it was clicked inside the editor
+    // (posFromMouse returning non-null), we have to adjust the
+    // selection.
+    if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
+
+    if (!cm.state.focused) onFocus(cm);
+
+    var now = +new Date, type = "single";
+    if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
+      type = "triple";
+      e_preventDefault(e);
+      setTimeout(bind(focusInput, cm), 20);
+      selectLine(cm, start.line);
+    } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
+      type = "double";
+      lastDoubleClick = {time: now, pos: start};
+      e_preventDefault(e);
+      var word = findWordAt(getLine(doc, start.line).text, start);
+      extendSelection(cm.doc, word.from, word.to);
+    } else { lastClick = {time: now, pos: start}; }
+
+    var last = start;
+    if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
+        !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
+      var dragEnd = operation(cm, function(e2) {
+        if (webkit) display.scroller.draggable = false;
+        cm.state.draggingText = false;
+        off(document, "mouseup", dragEnd);
+        off(display.scroller, "drop", dragEnd);
+        if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
+          e_preventDefault(e2);
+          extendSelection(cm.doc, start);
+          focusInput(cm);
+        }
+      });
+      // Let the drag handler handle this.
+      if (webkit) display.scroller.draggable = true;
+      cm.state.draggingText = dragEnd;
+      // IE's approach to draggable
+      if (display.scroller.dragDrop) display.scroller.dragDrop();
+      on(document, "mouseup", dragEnd);
+      on(display.scroller, "drop", dragEnd);
+      return;
+    }
+    e_preventDefault(e);
+    if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
+
+    var startstart = sel.from, startend = sel.to, lastPos = start;
+
+    function doSelect(cur) {
+      if (posEq(lastPos, cur)) return;
+      lastPos = cur;
+
+      if (type == "single") {
+        extendSelection(cm.doc, clipPos(doc, start), cur);
+        return;
+      }
+
+      startstart = clipPos(doc, startstart);
+      startend = clipPos(doc, startend);
+      if (type == "double") {
+        var word = findWordAt(getLine(doc, cur.line).text, cur);
+        if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
+        else extendSelection(cm.doc, startstart, word.to);
+      } else if (type == "triple") {
+        if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
+        else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
+      }
+    }
+
+    var editorSize = getRect(display.wrapper);
+    // Used to ensure timeout re-tries don't fire when another extend
+    // happened in the meantime (clearTimeout isn't reliable -- at
+    // least on Chrome, the timeouts still happen even when cleared,
+    // if the clear happens after their scheduled firing time).
+    var counter = 0;
+
+    function extend(e) {
+      var curCount = ++counter;
+      var cur = posFromMouse(cm, e, true);
+      if (!cur) return;
+      if (!posEq(cur, last)) {
+        if (!cm.state.focused) onFocus(cm);
+        last = cur;
+        doSelect(cur);
+        var visible = visibleLines(display, doc);
+        if (cur.line >= visible.to || cur.line < visible.from)
+          setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
+      } else {
+        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
+        if (outside) setTimeout(operation(cm, function() {
+          if (counter != curCount) return;
+          display.scroller.scrollTop += outside;
+          extend(e);
+        }), 50);
+      }
+    }
+
+    function done(e) {
+      counter = Infinity;
+      var cur = posFromMouse(cm, e);
+      if (cur) doSelect(cur);
+      e_preventDefault(e);
+      focusInput(cm);
+      off(document, "mousemove", move);
+      off(document, "mouseup", up);
+    }
+
+    var move = operation(cm, function(e) {
+      if (!ie && !e_button(e)) done(e);
+      else extend(e);
+    });
+    var up = operation(cm, done);
+    on(document, "mousemove", move);
+    on(document, "mouseup", up);
+  }
+
+  function onDrop(e) {
+    var cm = this;
+    if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
+      return;
+    e_preventDefault(e);
+    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
+    if (!pos || isReadOnly(cm)) return;
+    if (files && files.length && window.FileReader && window.File) {
+      var n = files.length, text = Array(n), read = 0;
+      var loadFile = function(file, i) {
+        var reader = new FileReader;
+        reader.onload = function() {
+          text[i] = reader.result;
+          if (++read == n) {
+            pos = clipPos(cm.doc, pos);
+            makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
+          }
+        };
+        reader.readAsText(file);
+      };
+      for (var i = 0; i < n; ++i) loadFile(files[i], i);
+    } else {
+      // Don't do a replace if the drop happened inside of the selected text.
+      if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
+        cm.state.draggingText(e);
+        // Ensure the editor is re-focused
+        setTimeout(bind(focusInput, cm), 20);
+        return;
+      }
+      try {
+        var text = e.dataTransfer.getData("Text");
+        if (text) {
+          var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
+          setSelection(cm.doc, pos, pos);
+          if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
+          cm.replaceSelection(text, null, "paste");
+          focusInput(cm);
+          onFocus(cm);
+        }
+      }
+      catch(e){}
+    }
+  }
+
+  function clickInGutter(cm, e) {
+    var display = cm.display;
+    try { var mX = e.clientX, mY = e.clientY; }
+    catch(e) { return false; }
+
+    if (mX >= Math.floor(getRect(display.gutters).right)) return false;
+    e_preventDefault(e);
+    if (!hasHandler(cm, "gutterClick")) return true;
+
+    var lineBox = getRect(display.lineDiv);
+    if (mY > lineBox.bottom) return true;
+    mY -= lineBox.top - display.viewOffset;
+
+    for (var i = 0; i < cm.options.gutters.length; ++i) {
+      var g = display.gutters.childNodes[i];
+      if (g && getRect(g).right >= mX) {
+        var line = lineAtHeight(cm.doc, mY);
+        var gutter = cm.options.gutters[i];
+        signalLater(cm, "gutterClick", cm, line, gutter, e);
+        break;
+      }
+    }
+    return true;
+  }
+
+  function onDragStart(cm, e) {
+    if (ie && !cm.state.draggingText) { e_stop(e); return; }
+    if (eventInWidget(cm.display, e)) return;
+
+    var txt = cm.getSelection();
+    e.dataTransfer.setData("Text", txt);
+
+    // Use dummy image instead of default browsers image.
+    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
+    if (e.dataTransfer.setDragImage) {
+      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
+      if (opera) {
+        img.width = img.height = 1;
+        cm.display.wrapper.appendChild(img);
+        // Force a relayout, or Opera won't use our image for some obscure reason
+        img._top = img.offsetTop;
+      }
+      if (safari) {
+        if (cm.display.dragImg) {
+          img = cm.display.dragImg;
+        } else {
+          cm.display.dragImg = img;
+          img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
+          cm.display.wrapper.appendChild(img);
+        }
+      }
+      e.dataTransfer.setDragImage(img, 0, 0);
+      if (opera) img.parentNode.removeChild(img);
+    }
+  }
+
+  function setScrollTop(cm, val) {
+    if (Math.abs(cm.doc.scrollTop - val) < 2) return;
+    cm.doc.scrollTop = val;
+    if (!gecko) updateDisplay(cm, [], val);
+    if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
+    if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
+    if (gecko) updateDisplay(cm, []);
+  }
+  function setScrollLeft(cm, val, isScroller) {
+    if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
+    val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
+    cm.doc.scrollLeft = val;
+    alignHorizontally(cm);
+    if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
+    if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
+  }
+
+  // Since the delta values reported on mouse wheel events are
+  // unstandardized between browsers and even browser versions, and
+  // generally horribly unpredictable, this code starts by measuring
+  // the scroll effect that the first few mouse wheel events have,
+  // and, from that, detects the way it can convert deltas to pixel
+  // offsets afterwards.
+  //
+  // The reason we want to know the amount a wheel event will scroll
+  // is that it gives us a chance to update the display before the
+  // actual scrolling happens, reducing flickering.
+
+  var wheelSamples = 0, wheelPixelsPerUnit = null;
+  // Fill in a browser-detected starting value on browsers where we
+  // know one. These don't have to be accurate -- the result of them
+  // being wrong would just be a slight flicker on the first wheel
+  // scroll (if it is large enough).
+  if (ie) wheelPixelsPerUnit = -.53;
+  else if (gecko) wheelPixelsPerUnit = 15;
+  else if (chrome) wheelPixelsPerUnit = -.7;
+  else if (safari) wheelPixelsPerUnit = -1/3;
+
+  function onScrollWheel(cm, e) {
+    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
+    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
+    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
+    else if (dy == null) dy = e.wheelDelta;
+
+    var display = cm.display, scroll = display.scroller;
+    // Quit if there's nothing to scroll here
+    if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
+          dy && scroll.scrollHeight > scroll.clientHeight)) return;
+
+    // Webkit browsers on OS X abort momentum scrolls when the target
+    // of the scroll event is removed from the scrollable element.
+    // This hack (see related code in patchDisplay) makes sure the
+    // element is kept around.
+    if (dy && mac && webkit) {
+      for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
+        if (cur.lineObj) {
+          cm.display.currentWheelTarget = cur;
+          break;
+        }
+      }
+    }
+
+    // On some browsers, horizontal scrolling will cause redraws to
+    // happen before the gutter has been realigned, causing it to
+    // wriggle around in a most unseemly way. When we have an
+    // estimated pixels/delta value, we just handle horizontal
+    // scrolling entirely here. It'll be slightly off from native, but
+    // better than glitching out.
+    if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
+      if (dy)
+        setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
+      setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
+      e_preventDefault(e);
+      display.wheelStartX = null; // Abort measurement, if in progress
+      return;
+    }
+
+    if (dy && wheelPixelsPerUnit != null) {
+      var pixels = dy * wheelPixelsPerUnit;
+      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
+      if (pixels < 0) top = Math.max(0, top + pixels - 50);
+      else bot = Math.min(cm.doc.height, bot + pixels + 50);
+      updateDisplay(cm, [], {top: top, bottom: bot});
+    }
+
+    if (wheelSamples < 20) {
+      if (display.wheelStartX == null) {
+        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
+        display.wheelDX = dx; display.wheelDY = dy;
+        setTimeout(function() {
+          if (display.wheelStartX == null) return;
+          var movedX = scroll.scrollLeft - display.wheelStartX;
+          var movedY = scroll.scrollTop - display.wheelStartY;
+          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
+            (movedX && display.wheelDX && movedX / display.wheelDX);
+          display.wheelStartX = display.wheelStartY = null;
+          if (!sample) return;
+          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
+          ++wheelSamples;
+        }, 200);
+      } else {
+        display.wheelDX += dx; display.wheelDY += dy;
+      }
+    }
+  }
+
+  function doHandleBinding(cm, bound, dropShift) {
+    if (typeof bound == "string") {
+      bound = commands[bound];
+      if (!bound) return false;
+    }
+    // Ensure previous input has been read, so that the handler sees a
+    // consistent view of the document
+    if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
+    var doc = cm.doc, prevShift = doc.sel.shift, done = false;
+    try {
+      if (isReadOnly(cm)) cm.state.suppressEdits = true;
+      if (dropShift) doc.sel.shift = false;
+      done = bound(cm) != Pass;
+    } finally {
+      doc.sel.shift = prevShift;
+      cm.state.suppressEdits = false;
+    }
+    return done;
+  }
+
+  function allKeyMaps(cm) {
+    var maps = cm.state.keyMaps.slice(0);
+    if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
+    maps.push(cm.options.keyMap);
+    return maps;
+  }
+
+  var maybeTransition;
+  function handleKeyBinding(cm, e) {
+    // Handle auto keymap transitions
+    var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
+    clearTimeout(maybeTransition);
+    if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
+      if (getKeyMap(cm.options.keyMap) == startMap)
+        cm.options.keyMap = (next.call ? next.call(null, cm) : next);
+    }, 50);
+
+    var name = keyName(e, true), handled = false;
+    if (!name) return false;
+    var keymaps = allKeyMaps(cm);
+
+    if (e.shiftKey) {
+      // First try to resolve full name (including 'Shift-'). Failing
+      // that, see if there is a cursor-motion command (starting with
+      // 'go') bound to the keyname without 'Shift-'.
+      handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
+             || lookupKey(name, keymaps, function(b) {
+                  if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
+                });
+    } else {
+      handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
+    }
+    if (handled == "stop") handled = false;
+
+    if (handled) {
+      e_preventDefault(e);
+      restartBlink(cm);
+      if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
+    }
+    return handled;
+  }
+
+  function handleCharBinding(cm, e, ch) {
+    var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
+                            function(b) { return doHandleBinding(cm, b, true); });
+    if (handled) {
+      e_preventDefault(e);
+      restartBlink(cm);
+    }
+    return handled;
+  }
+
+  var lastStoppedKey = null;
+  function onKeyDown(e) {
+    var cm = this;
+    if (!cm.state.focused) onFocus(cm);
+    if (ie && e.keyCode == 27) { e.returnValue = false; }
+    if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+    var code = e.keyCode;
+    // IE does strange things with escape.
+    cm.doc.sel.shift = code == 16 || e.shiftKey;
+    // First give onKeyEvent option a chance to handle this.
+    var handled = handleKeyBinding(cm, e);
+    if (opera) {
+      lastStoppedKey = handled ? code : null;
+      // Opera has no cut event... we try to at least catch the key combo
+      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
+        cm.replaceSelection("");
+    }
+  }
+
+  function onKeyPress(e) {
+    var cm = this;
+    if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+    var keyCode = e.keyCode, charCode = e.charCode;
+    if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
+    if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
+    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
+    if (this.options.electricChars && this.doc.mode.electricChars &&
+        this.options.smartIndent && !isReadOnly(this) &&
+        this.doc.mode.electricChars.indexOf(ch) > -1)
+      setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
+    if (handleCharBinding(cm, e, ch)) return;
+    if (ie && !ie_lt9) cm.display.inputHasSelection = null;
+    fastPoll(cm);
+  }
+
+  function onFocus(cm) {
+    if (cm.options.readOnly == "nocursor") return;
+    if (!cm.state.focused) {
+      signal(cm, "focus", cm);
+      cm.state.focused = true;
+      if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
+        cm.display.wrapper.className += " CodeMirror-focused";
+      resetInput(cm, true);
+    }
+    slowPoll(cm);
+    restartBlink(cm);
+  }
+  function onBlur(cm) {
+    if (cm.state.focused) {
+      signal(cm, "blur", cm);
+      cm.state.focused = false;
+      cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
+    }
+    clearInterval(cm.display.blinker);
+    setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
+  }
+
+  var detectingSelectAll;
+  function onContextMenu(cm, e) {
+    var display = cm.display, sel = cm.doc.sel;
+    if (eventInWidget(display, e)) return;
+
+    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
+    if (!pos || opera) return; // Opera is difficult.
+    if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
+      operation(cm, setSelection)(cm.doc, pos, pos);
+
+    var oldCSS = display.input.style.cssText;
+    display.inputDiv.style.position = "absolute";
+    display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
+      "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
+      "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
+    focusInput(cm);
+    resetInput(cm, true);
+    // Adds "Select all" to context menu in FF
+    if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
+
+    function rehide() {
+      display.inputDiv.style.position = "relative";
+      display.input.style.cssText = oldCSS;
+      if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
+      slowPoll(cm);
+
+      // Try to detect the user choosing select-all
+      if (display.input.selectionStart != null && (!ie || ie_lt9)) {
+        clearTimeout(detectingSelectAll);
+        var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
+        display.prevInput = " ";
+        display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
+        var poll = function(){
+          if (display.prevInput == " " && display.input.selectionStart == 0)
+            operation(cm, commands.selectAll)(cm);
+          else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
+          else resetInput(cm);
+        };
+        detectingSelectAll = setTimeout(poll, 200);
+      }
+    }
+
+    if (captureMiddleClick) {
+      e_stop(e);
+      var mouseup = function() {
+        off(window, "mouseup", mouseup);
+        setTimeout(rehide, 20);
+      };
+      on(window, "mouseup", mouseup);
+    } else {
+      setTimeout(rehide, 50);
+    }
+  }
+
+  // UPDATING
+
+  function changeEnd(change) {
+    if (!change.text) return change.to;
+    return Pos(change.from.line + change.text.length - 1,
+               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
+  }
+
+  // Make sure a position will be valid after the given change.
+  function clipPostChange(doc, change, pos) {
+    if (!posLess(change.from, pos)) return clipPos(doc, pos);
+    var diff = (change.text.length - 1) - (change.to.line - change.from.line);
+    if (pos.line > change.to.line + diff) {
+      var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
+      if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
+      return clipToLen(pos, getLine(doc, preLine).text.length);
+    }
+    if (pos.line == change.to.line + diff)
+      return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
+                       getLine(doc, change.to.line).text.length - change.to.ch);
+    var inside = pos.line - change.from.line;
+    return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
+  }
+
+  // Hint can be null|"end"|"start"|"around"|{anchor,head}
+  function computeSelAfterChange(doc, change, hint) {
+    if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
+      return {anchor: clipPostChange(doc, change, hint.anchor),
+              head: clipPostChange(doc, change, hint.head)};
+
+    if (hint == "start") return {anchor: change.from, head: change.from};
+
+    var end = changeEnd(change);
+    if (hint == "around") return {anchor: change.from, head: end};
+    if (hint == "end") return {anchor: end, head: end};
+
+    // hint is null, leave the selection alone as much as possible
+    var adjustPos = function(pos) {
+      if (posLess(pos, change.from)) return pos;
+      if (!posLess(change.to, pos)) return end;
+
+      var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
+      if (pos.line == change.to.line) ch += end.ch - change.to.ch;
+      return Pos(line, ch);
+    };
+    return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
+  }
+
+  function filterChange(doc, change) {
+    var obj = {
+      canceled: false,
+      from: change.from,
+      to: change.to,
+      text: change.text,
+      origin: change.origin,
+      update: function(from, to, text, origin) {
+        if (from) this.from = clipPos(doc, from);
+        if (to) this.to = clipPos(doc, to);
+        if (text) this.text = text;
+        if (origin !== undefined) this.origin = origin;
+      },
+      cancel: function() { this.canceled = true; }
+    };
+    signal(doc, "beforeChange", doc, obj);
+    if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
+
+    if (obj.canceled) return null;
+    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
+  }
+
+  // Replace the range from from to to by the strings in replacement.
+  // change is a {from, to, text [, origin]} object
+  function makeChange(doc, change, selUpdate, ignoreReadOnly) {
+    if (doc.cm) {
+      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
+      if (doc.cm.state.suppressEdits) return;
+    }
+
+    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
+      change = filterChange(doc, change);
+      if (!change) return;
+    }
+
+    // Possibly split or suppress the update based on the presence
+    // of read-only spans in its range.
+    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
+    if (split) {
+      for (var i = split.length - 1; i >= 1; --i)
+        makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
+      if (split.length)
+        makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
+    } else {
+      makeChangeNoReadonly(doc, change, selUpdate);
+    }
+  }
+
+  function makeChangeNoReadonly(doc, change, selUpdate) {
+    var selAfter = computeSelAfterChange(doc, change, selUpdate);
+    addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
+
+    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
+    var rebased = [];
+
+    linkedDocs(doc, function(doc, sharedHist) {
+      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+        rebaseHist(doc.history, change);
+        rebased.push(doc.history);
+      }
+      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
+    });
+  }
+
+  function makeChangeFromHistory(doc, type) {
+    if (doc.cm && doc.cm.state.suppressEdits) return;
+
+    var hist = doc.history;
+    var event = (type == "undo" ? hist.done : hist.undone).pop();
+    if (!event) return;
+    hist.dirtyCounter += type == "undo" ? -1 : 1;
+
+    var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
+                anchorAfter: event.anchorBefore, headAfter: event.headBefore};
+    (type == "undo" ? hist.undone : hist.done).push(anti);
+
+    for (var i = event.changes.length - 1; i >= 0; --i) {
+      var change = event.changes[i];
+      change.origin = type;
+      anti.changes.push(historyChangeFromChange(doc, change));
+
+      var after = i ? computeSelAfterChange(doc, change, null)
+                    : {anchor: event.anchorBefore, head: event.headBefore};
+      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
+      var rebased = [];
+
+      linkedDocs(doc, function(doc, sharedHist) {
+        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+          rebaseHist(doc.history, change);
+          rebased.push(doc.history);
+        }
+        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
+      });
+    }
+  }
+
+  function shiftDoc(doc, distance) {
+    function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
+    doc.first += distance;
+    if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
+    doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
+    doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
+  }
+
+  function makeChangeSingleDoc(doc, change, selAfter, spans) {
+    if (doc.cm && !doc.cm.curOp)
+      return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
+
+    if (change.to.line < doc.first) {
+      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
+      return;
+    }
+    if (change.from.line > doc.lastLine()) return;
+
+    // Clip the change to the size of this doc
+    if (change.from.line < doc.first) {
+      var shift = change.text.length - 1 - (doc.first - change.from.line);
+      shiftDoc(doc, shift);
+      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
+                text: [lst(change.text)], origin: change.origin};
+    }
+    var last = doc.lastLine();
+    if (change.to.line > last) {
+      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
+                text: [change.text[0]], origin: change.origin};
+    }
+
+    change.removed = getBetween(doc, change.from, change.to);
+
+    if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
+    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
+    else updateDoc(doc, change, spans, selAfter);
+  }
+
+  function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
+    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
+
+    var recomputeMaxLength = false, checkWidthStart = from.line;
+    if (!cm.options.lineWrapping) {
+      checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
+      doc.iter(checkWidthStart, to.line + 1, function(line) {
+        if (line == display.maxLine) {
+          recomputeMaxLength = true;
+          return true;
+        }
+      });
+    }
+
+    if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head))
+      cm.curOp.cursorActivity = true;
+
+    updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
+
+    if (!cm.options.lineWrapping) {
+      doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
+        var len = lineLength(doc, line);
+        if (len > display.maxLineLength) {
+          display.maxLine = line;
+          display.maxLineLength = len;
+          display.maxLineChanged = true;
+          recomputeMaxLength = false;
+        }
+      });
+      if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
+    }
+
+    // Adjust frontier, schedule worker
+    doc.frontier = Math.min(doc.frontier, from.line);
+    startWorker(cm, 400);
+
+    var lendiff = change.text.length - (to.line - from.line) - 1;
+    // Remember that these lines changed, for updating the display
+    regChange(cm, from.line, to.line + 1, lendiff);
+
+    if (hasHandler(cm, "change")) {
+      var changeObj = {from: from, to: to,
+                       text: change.text,
+                       removed: change.removed,
+                       origin: change.origin};
+      if (cm.curOp.textChanged) {
+        for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
+        cur.next = changeObj;
+      } else cm.curOp.textChanged = changeObj;
+    }
+  }
+
+  function replaceRange(doc, code, from, to, origin) {
+    if (!to) to = from;
+    if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
+    if (typeof code == "string") code = splitLines(code);
+    makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
+  }
+
+  // POSITION OBJECT
+
+  function Pos(line, ch) {
+    if (!(this instanceof Pos)) return new Pos(line, ch);
+    this.line = line; this.ch = ch;
+  }
+  CodeMirror.Pos = Pos;
+
+  function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
+  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
+  function copyPos(x) {return Pos(x.line, x.ch);}
+
+  // SELECTION
+
+  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
+  function clipPos(doc, pos) {
+    if (pos.line < doc.first) return Pos(doc.first, 0);
+    var last = doc.first + doc.size - 1;
+    if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
+    return clipToLen(pos, getLine(doc, pos.line).text.length);
+  }
+  function clipToLen(pos, linelen) {
+    var ch = pos.ch;
+    if (ch == null || ch > linelen) return Pos(pos.line, linelen);
+    else if (ch < 0) return Pos(pos.line, 0);
+    else return pos;
+  }
+  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
+
+  // If shift is held, this will move the selection anchor. Otherwise,
+  // it'll set the whole selection.
+  function extendSelection(doc, pos, other, bias) {
+    if (doc.sel.shift || doc.sel.extend) {
+      var anchor = doc.sel.anchor;
+      if (other) {
+        var posBefore = posLess(pos, anchor);
+        if (posBefore != posLess(other, anchor)) {
+          anchor = pos;
+          pos = other;
+        } else if (posBefore != posLess(pos, other)) {
+          pos = other;
+        }
+      }
+      setSelection(doc, anchor, pos, bias);
+    } else {
+      setSelection(doc, pos, other || pos, bias);
+    }
+    if (doc.cm) doc.cm.curOp.userSelChange = true;
+  }
+
+  function filterSelectionChange(doc, anchor, head) {
+    var obj = {anchor: anchor, head: head};
+    signal(doc, "beforeSelectionChange", doc, obj);
+    if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
+    obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
+    return obj;
+  }
+
+  // Update the selection. Last two args are only used by
+  // updateDoc, since they have to be expressed in the line
+  // numbers before the update.
+  function setSelection(doc, anchor, head, bias, checkAtomic) {
+    if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
+      var filtered = filterSelectionChange(doc, anchor, head);
+      head = filtered.head;
+      anchor = filtered.anchor;
+    }
+
+    var sel = doc.sel;
+    sel.goalColumn = null;
+    // Skip over atomic spans.
+    if (checkAtomic || !posEq(anchor, sel.anchor))
+      anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
+    if (checkAtomic || !posEq(head, sel.head))
+      head = skipAtomic(doc, head, bias, checkAtomic != "push");
+
+    if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
+
+    sel.anchor = anchor; sel.head = head;
+    var inv = posLess(head, anchor);
+    sel.from = inv ? head : anchor;
+    sel.to = inv ? anchor : head;
+
+    if (doc.cm)
+      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
+        doc.cm.curOp.cursorActivity = true;
+
+    signalLater(doc, "cursorActivity", doc);
+  }
+
+  function reCheckSelection(cm) {
+    setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
+  }
+
+  function skipAtomic(doc, pos, bias, mayClear) {
+    var flipped = false, curPos = pos;
+    var dir = bias || 1;
+    doc.cantEdit = false;
+    search: for (;;) {
+      var line = getLine(doc, curPos.line);
+      if (line.markedSpans) {
+        for (var i = 0; i < line.markedSpans.length; ++i) {
+          var sp = line.markedSpans[i], m = sp.marker;
+          if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
+              (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
+            if (mayClear) {
+              signal(m, "beforeCursorEnter");
+              if (m.explicitlyCleared) {
+                if (!line.markedSpans) break;
+                else {--i; continue;}
+              }
+            }
+            if (!m.atomic) continue;
+            var newPos = m.find()[dir < 0 ? "from" : "to"];
+            if (posEq(newPos, curPos)) {
+              newPos.ch += dir;
+              if (newPos.ch < 0) {
+                if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
+                else newPos = null;
+              } else if (newPos.ch > line.text.length) {
+                if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
+                else newPos = null;
+              }
+              if (!newPos) {
+                if (flipped) {
+                  // Driven in a corner -- no valid cursor position found at all
+                  // -- try again *with* clearing, if we didn't already
+                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
+                  // Otherwise, turn off editing until further notice, and return the start of the doc
+                  doc.cantEdit = true;
+                  return Pos(doc.first, 0);
+                }
+                flipped = true; newPos = pos; dir = -dir;
+              }
+            }
+            curPos = newPos;
+            continue search;
+          }
+        }
+      }
+      return curPos;
+    }
+  }
+
+  // SCROLLING
+
+  function scrollCursorIntoView(cm) {
+    var coords = scrollPosIntoView(cm, cm.doc.sel.head);
+    if (!cm.state.focused) return;
+    var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
+    if (coords.top + pTop + box.top < 0) doScroll = true;
+    else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
+    if (doScroll != null && !phantom) {
+      var hidden = display.cursor.style.display == "none";
+      if (hidden) {
+        display.cursor.style.display = "";
+        display.cursor.style.left = coords.left + "px";
+        display.cursor.style.top = (coords.top - display.viewOffset) + "px";
+      }
+      display.cursor.scrollIntoView(doScroll);
+      if (hidden) display.cursor.style.display = "none";
+    }
+  }
+
+  function scrollPosIntoView(cm, pos, margin) {
+    if (margin == null) margin = 0;
+    for (;;) {
+      var changed = false, coords = cursorCoords(cm, pos);
+      var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
+      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
+      if (scrollPos.scrollTop != null) {
+        setScrollTop(cm, scrollPos.scrollTop);
+        if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
+      }
+      if (scrollPos.scrollLeft != null) {
+        setScrollLeft(cm, scrollPos.scrollLeft);
+        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
+      }
+      if (!changed) return coords;
+    }
+  }
+
+  function scrollIntoView(cm, x1, y1, x2, y2) {
+    var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
+    if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
+    if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
+  }
+
+  function calculateScrollPos(cm, x1, y1, x2, y2) {
+    var display = cm.display, pt = paddingTop(display);
+    y1 += pt; y2 += pt;
+    if (y1 < 0) y1 = 0;
+    var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
+    var docBottom = cm.doc.height + paddingVert(display);
+    var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
+    if (y1 < screentop) {
+      result.scrollTop = atTop ? 0 : y1;
+    } else if (y2 > screentop + screen) {
+      var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
+      if (newTop != screentop) result.scrollTop = newTop;
+    }
+
+    var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
+    x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
+    var gutterw = display.gutters.offsetWidth;
+    var atLeft = x1 < gutterw + 10;
+    if (x1 < screenleft + gutterw || atLeft) {
+      if (atLeft) x1 = 0;
+      result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
+    } else if (x2 > screenw + screenleft - 3) {
+      result.scrollLeft = x2 + 10 - screenw;
+    }
+    return result;
+  }
+
+  function updateScrollPos(cm, left, top) {
+    cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
+                                scrollTop: top == null ? cm.doc.scrollTop : top};
+  }
+
+  function addToScrollPos(cm, left, top) {
+    var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
+    var scroll = cm.display.scroller;
+    pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
+    pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
+  }
+
+  // API UTILITIES
+
+  function indentLine(cm, n, how, aggressive) {
+    var doc = cm.doc;
+    if (!how) how = "add";
+    if (how == "smart") {
+      if (!cm.doc.mode.indent) how = "prev";
+      else var state = getStateBefore(cm, n);
+    }
+
+    var tabSize = cm.options.tabSize;
+    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
+    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
+    if (how == "smart") {
+      indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
+      if (indentation == Pass) {
+        if (!aggressive) return;
+        how = "prev";
+      }
+    }
+    if (how == "prev") {
+      if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
+      else indentation = 0;
+    } else if (how == "add") {
+      indentation = curSpace + cm.options.indentUnit;
+    } else if (how == "subtract") {
+      indentation = curSpace - cm.options.indentUnit;
+    }
+    indentation = Math.max(0, indentation);
+
+    var indentString = "", pos = 0;
+    if (cm.options.indentWithTabs)
+      for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
+    if (pos < indentation) indentString += spaceStr(indentation - pos);
+
+    if (indentString != curSpaceString)
+      replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
+    line.stateAfter = null;
+  }
+
+  function changeLine(cm, handle, op) {
+    var no = handle, line = handle, doc = cm.doc;
+    if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
+    else no = lineNo(handle);
+    if (no == null) return null;
+    if (op(line, no)) regChange(cm, no, no + 1);
+    else return null;
+    return line;
+  }
+
+  function findPosH(doc, pos, dir, unit, visually) {
+    var line = pos.line, ch = pos.ch;
+    var lineObj = getLine(doc, line);
+    var possible = true;
+    function findNextLine() {
+      var l = line + dir;
+      if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
+      line = l;
+      return lineObj = getLine(doc, l);
+    }
+    function moveOnce(boundToLine) {
+      var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
+      if (next == null) {
+        if (!boundToLine && findNextLine()) {
+          if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
+          else ch = dir < 0 ? lineObj.text.length : 0;
+        } else return (possible = false);
+      } else ch = next;
+      return true;
+    }
+
+    if (unit == "char") moveOnce();
+    else if (unit == "column") moveOnce(true);
+    else if (unit == "word" || unit == "group") {
+      var sawType = null, group = unit == "group";
+      for (var first = true;; first = false) {
+        if (dir < 0 && !moveOnce(!first)) break;
+        var cur = lineObj.text.charAt(ch) || "\n";
+        var type = isWordChar(cur) ? "w"
+          : !group ? null
+          : /\s/.test(cur) ? null
+          : "p";
+        if (sawType && sawType != type) {
+          if (dir < 0) {dir = 1; moveOnce();}
+          break;
+        }
+        if (type) sawType = type;
+        if (dir > 0 && !moveOnce(!first)) break;
+      }
+    }
+    var result = skipAtomic(doc, Pos(line, ch), dir, true);
+    if (!possible) result.hitSide = true;
+    return result;
+  }
+
+  function findPosV(cm, pos, dir, unit) {
+    var doc = cm.doc, x = pos.left, y;
+    if (unit == "page") {
+      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
+      y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
+    } else if (unit == "line") {
+      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
+    }
+    for (;;) {
+      var target = coordsChar(cm, x, y);
+      if (!target.outside) break;
+      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
+      y += dir * 5;
+    }
+    return target;
+  }
+
+  function findWordAt(line, pos) {
+    var start = pos.ch, end = pos.ch;
+    if (line) {
+      if (pos.after === false || end == line.length) --start; else ++end;
+      var startChar = line.charAt(start);
+      var check = isWordChar(startChar) ? isWordChar
+        : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
+        : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
+      while (start > 0 && check(line.charAt(start - 1))) --start;
+      while (end < line.length && check(line.charAt(end))) ++end;
+    }
+    return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
+  }
+
+  function selectLine(cm, line) {
+    extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
+  }
+
+  // PROTOTYPE
+
+  // The publicly visible API. Note that operation(null, f) means
+  // 'wrap f in an operation, performed on its `this` parameter'
+
+  CodeMirror.prototype = {
+    focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
+
+    setOption: function(option, value) {
+      var options = this.options, old = options[option];
+      if (options[option] == value && option != "mode") return;
+      options[option] = value;
+      if (optionHandlers.hasOwnProperty(option))
+        operation(this, optionHandlers[option])(this, value, old);
+    },
+
+    getOption: function(option) {return this.options[option];},
+    getDoc: function() {return this.doc;},
+
+    addKeyMap: function(map, bottom) {
+      this.state.keyMaps[bottom ? "push" : "unshift"](map);
+    },
+    removeKeyMap: function(map) {
+      var maps = this.state.keyMaps;
+      for (var i = 0; i < maps.length; ++i)
+        if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
+          maps.splice(i, 1);
+          return true;
+        }
+    },
+
+    addOverlay: operation(null, function(spec, options) {
+      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
+      if (mode.startState) throw new Error("Overlays may not be stateful.");
+      this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
+      this.state.modeGen++;
+      regChange(this);
+    }),
+    removeOverlay: operation(null, function(spec) {
+      var overlays = this.state.overlays;
+      for (var i = 0; i < overlays.length; ++i) {
+        if (overlays[i].modeSpec == spec) {
+          overlays.splice(i, 1);
+          this.state.modeGen++;
+          regChange(this);
+          return;
+        }
+      }
+    }),
+
+    indentLine: operation(null, function(n, dir, aggressive) {
+      if (typeof dir != "string") {
+        if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
+        else dir = dir ? "add" : "subtract";
+      }
+      if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
+    }),
+    indentSelection: operation(null, function(how) {
+      var sel = this.doc.sel;
+      if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
+      var e = sel.to.line - (sel.to.ch ? 0 : 1);
+      for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
+    }),
+
+    // Fetch the parser token for a given character. Useful for hacks
+    // that want to inspect the mode state (say, for completion).
+    getTokenAt: function(pos) {
+      var doc = this.doc;
+      pos = clipPos(doc, pos);
+      var state = getStateBefore(this, pos.line), mode = this.doc.mode;
+      var line = getLine(doc, pos.line);
+      var stream = new StringStream(line.text, this.options.tabSize);
+      while (stream.pos < pos.ch && !stream.eol()) {
+        stream.start = stream.pos;
+        var style = mode.token(stream, state);
+      }
+      return {start: stream.start,
+              end: stream.pos,
+              string: stream.current(),
+              className: style || null, // Deprecated, use 'type' instead
+              type: style || null,
+              state: state};
+    },
+
+    getStateAfter: function(line) {
+      var doc = this.doc;
+      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
+      return getStateBefore(this, line + 1);
+    },
+
+    cursorCoords: function(start, mode) {
+      var pos, sel = this.doc.sel;
+      if (start == null) pos = sel.head;
+      else if (typeof start == "object") pos = clipPos(this.doc, start);
+      else pos = start ? sel.from : sel.to;
+      return cursorCoords(this, pos, mode || "page");
+    },
+
+    charCoords: function(pos, mode) {
+      return charCoords(this, clipPos(this.doc, pos), mode || "page");
+    },
+
+    coordsChar: function(coords, mode) {
+      coords = fromCoordSystem(this, coords, mode || "page");
+      return coordsChar(this, coords.left, coords.top);
+    },
+
+    defaultTextHeight: function() { return textHeight(this.display); },
+    defaultCharWidth: function() { return charWidth(this.display); },
+
+    setGutterMarker: operation(null, function(line, gutterID, value) {
+      return changeLine(this, line, function(line) {
+        var markers = line.gutterMarkers || (line.gutterMarkers = {});
+        markers[gutterID] = value;
+        if (!value && isEmpty(markers)) line.gutterMarkers = null;
+        return true;
+      });
+    }),
+
+    clearGutter: operation(null, function(gutterID) {
+      var cm = this, doc = cm.doc, i = doc.first;
+      doc.iter(function(line) {
+        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
+          line.gutterMarkers[gutterID] = null;
+          regChange(cm, i, i + 1);
+          if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
+        }
+        ++i;
+      });
+    }),
+
+    addLineClass: operation(null, function(handle, where, cls) {
+      return changeLine(this, handle, function(line) {
+        var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
+        if (!line[prop]) line[prop] = cls;
+        else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
+        else line[prop] += " " + cls;
+        return true;
+      });
+    }),
+
+    removeLineClass: operation(null, function(handle, where, cls) {
+      return changeLine(this, handle, function(line) {
+        var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
+        var cur = line[prop];
+        if (!cur) return false;
+        else if (cls == null) line[prop] = null;
+        else {
+          var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
+          if (upd == cur) return false;
+          line[prop] = upd || null;
+        }
+        return true;
+      });
+    }),
+
+    addLineWidget: operation(null, function(handle, node, options) {
+      return addLineWidget(this, handle, node, options);
+    }),
+
+    removeLineWidget: function(widget) { widget.clear(); },
+
+    lineInfo: function(line) {
+      if (typeof line == "number") {
+        if (!isLine(this.doc, line)) return null;
+        var n = line;
+        line = getLine(this.doc, line);
+        if (!line) return null;
+      } else {
+        var n = lineNo(line);
+        if (n == null) return null;
+      }
+      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
+              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
+              widgets: line.widgets};
+    },
+
+    getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
+
+    addWidget: function(pos, node, scroll, vert, horiz) {
+      var display = this.display;
+      pos = cursorCoords(this, clipPos(this.doc, pos));
+      var top = pos.bottom, left = pos.left;
+      node.style.position = "absolute";
+      display.sizer.appendChild(node);
+      if (vert == "over") {
+        top = pos.top;
+      } else if (vert == "above" || vert == "near") {
+        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
+        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
+        // Default to positioning above (if specified and possible); otherwise default to positioning below
+        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
+          top = pos.top - node.offsetHeight;
+        else if (pos.bottom + node.offsetHeight <= vspace)
+          top = pos.bottom;
+        if (left + node.offsetWidth > hspace)
+          left = hspace - node.offsetWidth;
+      }
+      node.style.top = (top + paddingTop(display)) + "px";
+      node.style.left = node.style.right = "";
+      if (horiz == "right") {
+        left = display.sizer.clientWidth - node.offsetWidth;
+        node.style.right = "0px";
+      } else {
+        if (horiz == "left") left = 0;
+        else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
+        node.style.left = left + "px";
+      }
+      if (scroll)
+        scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
+    },
+
+    triggerOnKeyDown: operation(null, onKeyDown),
+
+    execCommand: function(cmd) {return commands[cmd](this);},
+
+    findPosH: function(from, amount, unit, visually) {
+      var dir = 1;
+      if (amount < 0) { dir = -1; amount = -amount; }
+      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
+        cur = findPosH(this.doc, cur, dir, unit, visually);
+        if (cur.hitSide) break;
+      }
+      return cur;
+    },
+
+    moveH: operation(null, function(dir, unit) {
+      var sel = this.doc.sel, pos;
+      if (sel.shift || sel.extend || posEq(sel.from, sel.to))
+        pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
+      else
+        pos = dir < 0 ? sel.from : sel.to;
+      extendSelection(this.doc, pos, pos, dir);
+    }),
+
+    deleteH: operation(null, function(dir, unit) {
+      var sel = this.doc.sel;
+      if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
+      else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
+      this.curOp.userSelChange = true;
+    }),
+
+    findPosV: function(from, amount, unit, goalColumn) {
+      var dir = 1, x = goalColumn;
+      if (amount < 0) { dir = -1; amount = -amount; }
+      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
+        var coords = cursorCoords(this, cur, "div");
+        if (x == null) x = coords.left;
+        else coords.left = x;
+        cur = findPosV(this, coords, dir, unit);
+        if (cur.hitSide) break;
+      }
+      return cur;
+    },
+
+    moveV: operation(null, function(dir, unit) {
+      var sel = this.doc.sel;
+      var pos = cursorCoords(this, sel.head, "div");
+      if (sel.goalColumn != null) pos.left = sel.goalColumn;
+      var target = findPosV(this, pos, dir, unit);
+
+      if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
+      extendSelection(this.doc, target, target, dir);
+      sel.goalColumn = pos.left;
+    }),
+
+    toggleOverwrite: function() {
+      if (this.state.overwrite = !this.state.overwrite)
+        this.display.cursor.className += " CodeMirror-overwrite";
+      else
+        this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
+    },
+    hasFocus: function() { return this.state.focused; },
+
+    scrollTo: operation(null, function(x, y) {
+      updateScrollPos(this, x, y);
+    }),
+    getScrollInfo: function() {
+      var scroller = this.display.scroller, co = scrollerCutOff;
+      return {left: scroller.scrollLeft, top: scroller.scrollTop,
+              height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
+              clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
+    },
+
+    scrollIntoView: operation(null, function(pos, margin) {
+      if (typeof pos == "number") pos = Pos(pos, 0);
+      if (!margin) margin = 0;
+      var coords = pos;
+
+      if (!pos || pos.line != null) {
+        this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
+        this.curOp.scrollToPosMargin = margin;
+        coords = cursorCoords(this, this.curOp.scrollToPos);
+      }
+      var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
+      updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
+    }),
+
+    setSize: function(width, height) {
+      function interpret(val) {
+        return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
+      }
+      if (width != null) this.display.wrapper.style.width = interpret(width);
+      if (height != null) this.display.wrapper.style.height = interpret(height);
+      this.refresh();
+    },
+
+    on: function(type, f) {on(this, type, f);},
+    off: function(type, f) {off(this, type, f);},
+
+    operation: function(f){return runInOp(this, f);},
+
+    refresh: operation(null, function() {
+      clearCaches(this);
+      updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
+      regChange(this);
+    }),
+
+    swapDoc: operation(null, function(doc) {
+      var old = this.doc;
+      old.cm = null;
+      attachDoc(this, doc);
+      clearCaches(this);
+      resetInput(this, true);
+      updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
+      return old;
+    }),
+
+    getInputField: function(){return this.display.input;},
+    getWrapperElement: function(){return this.display.wrapper;},
+    getScrollerElement: function(){return this.display.scroller;},
+    getGutterElement: function(){return this.display.gutters;}
+  };
+
+  // OPTION DEFAULTS
+
+  var optionHandlers = CodeMirror.optionHandlers = {};
+
+  // The default configuration options.
+  var defaults = CodeMirror.defaults = {};
+
+  function option(name, deflt, handle, notOnInit) {
+    CodeMirror.defaults[name] = deflt;
+    if (handle) optionHandlers[name] =
+      notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
+  }
+
+  var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
+
+  // These two are, on init, called from the constructor because they
+  // have to be initialized before the editor can start at all.
+  option("value", "", function(cm, val) {
+    cm.setValue(val);
+  }, true);
+  option("mode", null, function(cm, val) {
+    cm.doc.modeOption = val;
+    loadMode(cm);
+  }, true);
+
+  option("indentUnit", 2, loadMode, true);
+  option("indentWithTabs", false);
+  option("smartIndent", true);
+  option("tabSize", 4, function(cm) {
+    loadMode(cm);
+    clearCaches(cm);
+    regChange(cm);
+  }, true);
+  option("electricChars", true);
+  option("rtlMoveVisually", !windows);
+
+  option("theme", "default", function(cm) {
+    themeChanged(cm);
+    guttersChanged(cm);
+  }, true);
+  option("keyMap", "default", keyMapChanged);
+  option("extraKeys", null);
+
+  option("onKeyEvent", null);
+  option("onDragEvent", null);
+
+  option("lineWrapping", false, wrappingChanged, true);
+  option("gutters", [], function(cm) {
+    setGuttersForLineNumbers(cm.options);
+    guttersChanged(cm);
+  }, true);
+  option("fixedGutter", true, function(cm, val) {
+    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
+    cm.refresh();
+  }, true);
+  option("lineNumbers", false, function(cm) {
+    setGuttersForLineNumbers(cm.options);
+    guttersChanged(cm);
+  }, true);
+  option("firstLineNumber", 1, guttersChanged, true);
+  option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
+  option("showCursorWhenSelecting", false, updateSelection, true);
+
+  option("readOnly", false, function(cm, val) {
+    if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
+    else if (!val) resetInput(cm, true);
+  });
+  option("dragDrop", true);
+
+  option("cursorBlinkRate", 530);
+  option("cursorHeight", 1);
+  option("workTime", 100);
+  option("workDelay", 100);
+  option("flattenSpans", true);
+  option("pollInterval", 100);
+  option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
+  option("historyEventDelay", 500);
+  option("viewportMargin", 10, function(cm){cm.refresh();}, true);
+  option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
+  option("moveInputWithCursor", true, function(cm, val) {
+    if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
+  });
+
+  option("tabindex", null, function(cm, val) {
+    cm.display.input.tabIndex = val || "";
+  });
+  option("autofocus", null);
+
+  // MODE DEFINITION AND QUERYING
+
+  // Known modes, by name and by MIME
+  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
+
+  CodeMirror.defineMode = function(name, mode) {
+    if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
+    if (arguments.length > 2) {
+      mode.dependencies = [];
+      for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
+    }
+    modes[name] = mode;
+  };
+
+  CodeMirror.defineMIME = function(mime, spec) {
+    mimeModes[mime] = spec;
+  };
+
+  CodeMirror.resolveMode = function(spec) {
+    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
+      spec = mimeModes[spec];
+    else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
+      return CodeMirror.resolveMode("application/xml");
+    if (typeof spec == "string") return {name: spec};
+    else return spec || {name: "null"};
+  };
+
+  CodeMirror.getMode = function(options, spec) {
+    spec = CodeMirror.resolveMode(spec);
+    var mfactory = modes[spec.name];
+    if (!mfactory) return CodeMirror.getMode(options, "text/plain");
+    var modeObj = mfactory(options, spec);
+    if (modeExtensions.hasOwnProperty(spec.name)) {
+      var exts = modeExtensions[spec.name];
+      for (var prop in exts) {
+        if (!exts.hasOwnProperty(prop)) continue;
+        if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
+        modeObj[prop] = exts[prop];
+      }
+    }
+    modeObj.name = spec.name;
+    return modeObj;
+  };
+
+  CodeMirror.defineMode("null", function() {
+    return {token: function(stream) {stream.skipToEnd();}};
+  });
+  CodeMirror.defineMIME("text/plain", "null");
+
+  var modeExtensions = CodeMirror.modeExtensions = {};
+  CodeMirror.extendMode = function(mode, properties) {
+    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
+    copyObj(properties, exts);
+  };
+
+  // EXTENSIONS
+
+  CodeMirror.defineExtension = function(name, func) {
+    CodeMirror.prototype[name] = func;
+  };
+  CodeMirror.defineDocExtension = function(name, func) {
+    Doc.prototype[name] = func;
+  };
+  CodeMirror.defineOption = option;
+
+  var initHooks = [];
+  CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
+
+  // MODE STATE HANDLING
+
+  // Utility functions for working with state. Exported because modes
+  // sometimes need to do this.
+  function copyState(mode, state) {
+    if (state === true) return state;
+    if (mode.copyState) return mode.copyState(state);
+    var nstate = {};
+    for (var n in state) {
+      var val = state[n];
+      if (val instanceof Array) val = val.concat([]);
+      nstate[n] = val;
+    }
+    return nstate;
+  }
+  CodeMirror.copyState = copyState;
+
+  function startState(mode, a1, a2) {
+    return mode.startState ? mode.startState(a1, a2) : true;
+  }
+  CodeMirror.startState = startState;
+
+  CodeMirror.innerMode = function(mode, state) {
+    while (mode.innerMode) {
+      var info = mode.innerMode(state);
+      state = info.state;
+      mode = info.mode;
+    }
+    return info || {mode: mode, state: state};
+  };
+
+  // STANDARD COMMANDS
+
+  var commands = CodeMirror.commands = {
+    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
+    killLine: function(cm) {
+      var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
+      if (!sel && cm.getLine(from.line).length == from.ch)
+        cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
+      else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
+    },
+    deleteLine: function(cm) {
+      var l = cm.getCursor().line;
+      cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
+    },
+    undo: function(cm) {cm.undo();},
+    redo: function(cm) {cm.redo();},
+    goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
+    goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
+    goLineStart: function(cm) {
+      cm.extendSelection(lineStart(cm, cm.getCursor().line));
+    },
+    goLineStartSmart: function(cm) {
+      var cur = cm.getCursor(), start = lineStart(cm, cur.line);
+      var line = cm.getLineHandle(start.line);
+      var order = getOrder(line);
+      if (!order || order[0].level == 0) {
+        var firstNonWS = Math.max(0, line.text.search(/\S/));
+        var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
+        cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
+      } else cm.extendSelection(start);
+    },
+    goLineEnd: function(cm) {
+      cm.extendSelection(lineEnd(cm, cm.getCursor().line));
+    },
+    goLineRight: function(cm) {
+      var top = cm.charCoords(cm.getCursor(), "div").top + 5;
+      cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
+    },
+    goLineLeft: function(cm) {
+      var top = cm.charCoords(cm.getCursor(), "div").top + 5;
+      cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
+    },
+    goLineUp: function(cm) {cm.moveV(-1, "line");},
+    goLineDown: function(cm) {cm.moveV(1, "line");},
+    goPageUp: function(cm) {cm.moveV(-1, "page");},
+    goPageDown: function(cm) {cm.moveV(1, "page");},
+    goCharLeft: function(cm) {cm.moveH(-1, "char");},
+    goCharRight: function(cm) {cm.moveH(1, "char");},
+    goColumnLeft: function(cm) {cm.moveH(-1, "column");},
+    goColumnRight: function(cm) {cm.moveH(1, "column");},
+    goWordLeft: function(cm) {cm.moveH(-1, "word");},
+    goGroupRight: function(cm) {cm.moveH(1, "group");},
+    goGroupLeft: function(cm) {cm.moveH(-1, "group");},
+    goWordRight: function(cm) {cm.moveH(1, "word");},
+    delCharBefore: function(cm) {cm.deleteH(-1, "char");},
+    delCharAfter: function(cm) {cm.deleteH(1, "char");},
+    delWordBefore: function(cm) {cm.deleteH(-1, "word");},
+    delWordAfter: function(cm) {cm.deleteH(1, "word");},
+    delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
+    delGroupAfter: function(cm) {cm.deleteH(1, "group");},
+    indentAuto: function(cm) {cm.indentSelection("smart");},
+    indentMore: function(cm) {cm.indentSelection("add");},
+    indentLess: function(cm) {cm.indentSelection("subtract");},
+    insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
+    defaultTab: function(cm) {
+      if (cm.somethingSelected()) cm.indentSelection("add");
+      else cm.replaceSelection("\t", "end", "+input");
+    },
+    transposeChars: function(cm) {
+      var cur = cm.getCursor(), line = cm.getLine(cur.line);
+      if (cur.ch > 0 && cur.ch < line.length - 1)
+        cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
+                        Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
+    },
+    newlineAndIndent: function(cm) {
+      operation(cm, function() {
+        cm.replaceSelection("\n", "end", "+input");
+        cm.indentLine(cm.getCursor().line, null, true);
+      })();
+    },
+    toggleOverwrite: function(cm) {cm.toggleOverwrite();}
+  };
+
+  // STANDARD KEYMAPS
+
+  var keyMap = CodeMirror.keyMap = {};
+  keyMap.basic = {
+    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
+    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
+    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
+    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
+  };
+  // Note that the save and find-related commands aren't defined by
+  // default. Unknown commands are simply ignored.
+  keyMap.pcDefault = {
+    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
+    "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
+    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
+    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
+    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
+    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
+    fallthrough: "basic"
+  };
+  keyMap.macDefault = {
+    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
+    "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
+    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
+    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
+    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
+    "Cmd-[": "indentLess", "Cmd-]": "indentMore",
+    fallthrough: ["basic", "emacsy"]
+  };
+  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
+  keyMap.emacsy = {
+    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
+    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
+    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
+    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
+  };
+
+  // KEYMAP DISPATCH
+
+  function getKeyMap(val) {
+    if (typeof val == "string") return keyMap[val];
+    else return val;
+  }
+
+  function lookupKey(name, maps, handle) {
+    function lookup(map) {
+      map = getKeyMap(map);
+      var found = map[name];
+      if (found === false) return "stop";
+      if (found != null && handle(found)) return true;
+      if (map.nofallthrough) return "stop";
+
+      var fallthrough = map.fallthrough;
+      if (fallthrough == null) return false;
+      if (Object.prototype.toString.call(fallthrough) != "[object Array]")
+        return lookup(fallthrough);
+      for (var i = 0, e = fallthrough.length; i < e; ++i) {
+        var done = lookup(fallthrough[i]);
+        if (done) return done;
+      }
+      return false;
+    }
+
+    for (var i = 0; i < maps.length; ++i) {
+      var done = lookup(maps[i]);
+      if (done) return done;
+    }
+  }
+  function isModifierKey(event) {
+    var name = keyNames[event.keyCode];
+    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
+  }
+  function keyName(event, noShift) {
+    if (opera && event.keyCode == 34 && event["char"]) return false;
+    var name = keyNames[event.keyCode];
+    if (name == null || event.altGraphKey) return false;
+    if (event.altKey) name = "Alt-" + name;
+    if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
+    if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
+    if (!noShift && event.shiftKey) name = "Shift-" + name;
+    return name;
+  }
+  CodeMirror.lookupKey = lookupKey;
+  CodeMirror.isModifierKey = isModifierKey;
+  CodeMirror.keyName = keyName;
+
+  // FROMTEXTAREA
+
+  CodeMirror.fromTextArea = function(textarea, options) {
+    if (!options) options = {};
+    options.value = textarea.value;
+    if (!options.tabindex && textarea.tabindex)
+      options.tabindex = textarea.tabindex;
+    if (!options.placeholder && textarea.placeholder)
+      options.placeholder = textarea.placeholder;
+    // Set autofocus to true if this textarea is focused, or if it has
+    // autofocus and no other element is focused.
+    if (options.autofocus == null) {
+      var hasFocus = document.body;
+      // doc.activeElement occasionally throws on IE
+      try { hasFocus = document.activeElement; } catch(e) {}
+      options.autofocus = hasFocus == textarea ||
+        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
+    }
+
+    function save() {textarea.value = cm.getValue();}
+    if (textarea.form) {
+      on(textarea.form, "submit", save);
+      // Deplorable hack to make the submit method do the right thing.
+      if (!options.leaveSubmitMethodAlone) {
+        var form = textarea.form, realSubmit = form.submit;
+        try {
+          var wrappedSubmit = form.submit = function() {
+            save();
+            form.submit = realSubmit;
+            form.submit();
+            form.submit = wrappedSubmit;
+          };
+        } catch(e) {}
+      }
+    }
+
+    textarea.style.display = "none";
+    var cm = CodeMirror(function(node) {
+      textarea.parentNode.insertBefore(node, textarea.nextSibling);
+    }, options);
+    cm.save = save;
+    cm.getTextArea = function() { return textarea; };
+    cm.toTextArea = function() {
+      save();
+      textarea.parentNode.removeChild(cm.getWrapperElement());
+      textarea.style.display = "";
+      if (textarea.form) {
+        off(textarea.form, "submit", save);
+        if (typeof textarea.form.submit == "function")
+          textarea.form.submit = realSubmit;
+      }
+    };
+    return cm;
+  };
+
+  // STRING STREAM
+
+  // Fed to the mode parsers, provides helper functions to make
+  // parsers more succinct.
+
+  // The character stream used by a mode's parser.
+  function StringStream(string, tabSize) {
+    this.pos = this.start = 0;
+    this.string = string;
+    this.tabSize = tabSize || 8;
+    this.lastColumnPos = this.lastColumnValue = 0;
+  }
+
+  StringStream.prototype = {
+    eol: function() {return this.pos >= this.string.length;},
+    sol: function() {return this.pos == 0;},
+    peek: function() {return this.string.charAt(this.pos) || undefined;},
+    next: function() {
+      if (this.pos < this.string.length)
+        return this.string.charAt(this.pos++);
+    },
+    eat: function(match) {
+      var ch = this.string.charAt(this.pos);
+      if (typeof match == "string") var ok = ch == match;
+      else var ok = ch && (match.test ? match.test(ch) : match(ch));
+      if (ok) {++this.pos; return ch;}
+    },
+    eatWhile: function(match) {
+      var start = this.pos;
+      while (this.eat(match)){}
+      return this.pos > start;
+    },
+    eatSpace: function() {
+      var start = this.pos;
+      while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+      return this.pos > start;
+    },
+    skipToEnd: function() {this.pos = this.string.length;},
+    skipTo: function(ch) {
+      var found = this.string.indexOf(ch, this.pos);
+      if (found > -1) {this.pos = found; return true;}
+    },
+    backUp: function(n) {this.pos -= n;},
+    column: function() {
+      if (this.lastColumnPos < this.start) {
+        this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
+        this.lastColumnPos = this.start;
+      }
+      return this.lastColumnValue;
+    },
+    indentation: function() {return countColumn(this.string, null, this.tabSize);},
+    match: function(pattern, consume, caseInsensitive) {
+      if (typeof pattern == "string") {
+        var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
+        var substr = this.string.substr(this.pos, pattern.length);
+        if (cased(substr) == cased(pattern)) {
+          if (consume !== false) this.pos += pattern.length;
+          return true;
+        }
+      } else {
+        var match = this.string.slice(this.pos).match(pattern);
+        if (match && match.index > 0) return null;
+        if (match && consume !== false) this.pos += match[0].length;
+        return match;
+      }
+    },
+    current: function(){return this.string.slice(this.start, this.pos);}
+  };
+  CodeMirror.StringStream = StringStream;
+
+  // TEXTMARKERS
+
+  function TextMarker(doc, type) {
+    this.lines = [];
+    this.type = type;
+    this.doc = doc;
+  }
+  CodeMirror.TextMarker = TextMarker;
+
+  TextMarker.prototype.clear = function() {
+    if (this.explicitlyCleared) return;
+    var cm = this.doc.cm, withOp = cm && !cm.curOp;
+    if (withOp) startOperation(cm);
+    var min = null, max = null;
+    for (var i = 0; i < this.lines.length; ++i) {
+      var line = this.lines[i];
+      var span = getMarkedSpanFor(line.markedSpans, this);
+      if (span.to != null) max = lineNo(line);
+      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
+      if (span.from != null)
+        min = lineNo(line);
+      else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
+        updateLineHeight(line, textHeight(cm.display));
+    }
+    if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
+      var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
+      if (len > cm.display.maxLineLength) {
+        cm.display.maxLine = visual;
+        cm.display.maxLineLength = len;
+        cm.display.maxLineChanged = true;
+      }
+    }
+
+    if (min != null && cm) regChange(cm, min, max + 1);
+    this.lines.length = 0;
+    this.explicitlyCleared = true;
+    if (this.collapsed && this.doc.cantEdit) {
+      this.doc.cantEdit = false;
+      if (cm) reCheckSelection(cm);
+    }
+    if (withOp) endOperation(cm);
+    signalLater(this, "clear");
+  };
+
+  TextMarker.prototype.find = function() {
+    var from, to;
+    for (var i = 0; i < this.lines.length; ++i) {
+      var line = this.lines[i];
+      var span = getMarkedSpanFor(line.markedSpans, this);
+      if (span.from != null || span.to != null) {
+        var found = lineNo(line);
+        if (span.from != null) from = Pos(found, span.from);
+        if (span.to != null) to = Pos(found, span.to);
+      }
+    }
+    if (this.type == "bookmark") return from;
+    return from && {from: from, to: to};
+  };
+
+  TextMarker.prototype.getOptions = function(copyWidget) {
+    var repl = this.replacedWith;
+    return {className: this.className,
+            inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
+            atomic: this.atomic,
+            collapsed: this.collapsed,
+            replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
+            readOnly: this.readOnly,
+            startStyle: this.startStyle, endStyle: this.endStyle};
+  };
+
+  TextMarker.prototype.attachLine = function(line) {
+    if (!this.lines.length && this.doc.cm) {
+      var op = this.doc.cm.curOp;
+      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
+        (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
+    }
+    this.lines.push(line);
+  };
+  TextMarker.prototype.detachLine = function(line) {
+    this.lines.splice(indexOf(this.lines, line), 1);
+    if (!this.lines.length && this.doc.cm) {
+      var op = this.doc.cm.curOp;
+      (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
+    }
+  };
+
+  function markText(doc, from, to, options, type) {
+    if (options && options.shared) return markTextShared(doc, from, to, options, type);
+    if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
+
+    var marker = new TextMarker(doc, type);
+    if (type == "range" && !posLess(from, to)) return marker;
+    if (options) copyObj(options, marker);
+    if (marker.replacedWith) {
+      marker.collapsed = true;
+      marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
+    }
+    if (marker.collapsed) sawCollapsedSpans = true;
+
+    if (marker.addToHistory)
+      addToHistory(doc, {from: from, to: to, origin: "markText"},
+                   {head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
+
+    var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
+    doc.iter(curLine, to.line + 1, function(line) {
+      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
+        updateMaxLine = true;
+      var span = {from: null, to: null, marker: marker};
+      size += line.text.length;
+      if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
+      if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
+      if (marker.collapsed) {
+        if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
+        if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
+        else updateLineHeight(line, 0);
+      }
+      addMarkedSpan(line, span);
+      ++curLine;
+    });
+    if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
+      if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
+    });
+
+    if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
+
+    if (marker.readOnly) {
+      sawReadOnlySpans = true;
+      if (doc.history.done.length || doc.history.undone.length)
+        doc.clearHistory();
+    }
+    if (marker.collapsed) {
+      if (collapsedAtStart != collapsedAtEnd)
+        throw new Error("Inserting collapsed marker overlapping an existing one");
+      marker.size = size;
+      marker.atomic = true;
+    }
+    if (cm) {
+      if (updateMaxLine) cm.curOp.updateMaxLine = true;
+      if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
+        regChange(cm, from.line, to.line + 1);
+      if (marker.atomic) reCheckSelection(cm);
+    }
+    return marker;
+  }
+
+  // SHARED TEXTMARKERS
+
+  function SharedTextMarker(markers, primary) {
+    this.markers = markers;
+    this.primary = primary;
+    for (var i = 0, me = this; i < markers.length; ++i) {
+      markers[i].parent = this;
+      on(markers[i], "clear", function(){me.clear();});
+    }
+  }
+  CodeMirror.SharedTextMarker = SharedTextMarker;
+
+  SharedTextMarker.prototype.clear = function() {
+    if (this.explicitlyCleared) return;
+    this.explicitlyCleared = true;
+    for (var i = 0; i < this.markers.length; ++i)
+      this.markers[i].clear();
+    signalLater(this, "clear");
+  };
+  SharedTextMarker.prototype.find = function() {
+    return this.primary.find();
+  };
+  SharedTextMarker.prototype.getOptions = function(copyWidget) {
+    var inner = this.primary.getOptions(copyWidget);
+    inner.shared = true;
+    return inner;
+  };
+
+  function markTextShared(doc, from, to, options, type) {
+    options = copyObj(options);
+    options.shared = false;
+    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
+    var widget = options.replacedWith;
+    linkedDocs(doc, function(doc) {
+      if (widget) options.replacedWith = widget.cloneNode(true);
+      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
+      for (var i = 0; i < doc.linked.length; ++i)
+        if (doc.linked[i].isParent) return;
+      primary = lst(markers);
+    });
+    return new SharedTextMarker(markers, primary);
+  }
+
+  // TEXTMARKER SPANS
+
+  function getMarkedSpanFor(spans, marker) {
+    if (spans) for (var i = 0; i < spans.length; ++i) {
+      var span = spans[i];
+      if (span.marker == marker) return span;
+    }
+  }
+  function removeMarkedSpan(spans, span) {
+    for (var r, i = 0; i < spans.length; ++i)
+      if (spans[i] != span) (r || (r = [])).push(spans[i]);
+    return r;
+  }
+  function addMarkedSpan(line, span) {
+    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
+    span.marker.attachLine(line);
+  }
+
+  function markedSpansBefore(old, startCh, isInsert) {
+    if (old) for (var i = 0, nw; i < old.length; ++i) {
+      var span = old[i], marker = span.marker;
+      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
+      if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
+        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
+        (nw || (nw = [])).push({from: span.from,
+                                to: endsAfter ? null : span.to,
+                                marker: marker});
+      }
+    }
+    return nw;
+  }
+
+  function markedSpansAfter(old, endCh, isInsert) {
+    if (old) for (var i = 0, nw; i < old.length; ++i) {
+      var span = old[i], marker = span.marker;
+      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
+      if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
+        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
+        (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
+                                to: span.to == null ? null : span.to - endCh,
+                                marker: marker});
+      }
+    }
+    return nw;
+  }
+
+  function stretchSpansOverChange(doc, change) {
+    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
+    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
+    if (!oldFirst && !oldLast) return null;
+
+    var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
+    // Get the spans that 'stick out' on both sides
+    var first = markedSpansBefore(oldFirst, startCh, isInsert);
+    var last = markedSpansAfter(oldLast, endCh, isInsert);
+
+    // Next, merge those two ends
+    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
+    if (first) {
+      // Fix up .to properties of first
+      for (var i = 0; i < first.length; ++i) {
+        var span = first[i];
+        if (span.to == null) {
+          var found = getMarkedSpanFor(last, span.marker);
+          if (!found) span.to = startCh;
+          else if (sameLine) span.to = found.to == null ? null : found.to + offset;
+        }
+      }
+    }
+    if (last) {
+      // Fix up .from in last (or move them into first in case of sameLine)
+      for (var i = 0; i < last.length; ++i) {
+        var span = last[i];
+        if (span.to != null) span.to += offset;
+        if (span.from == null) {
+          var found = getMarkedSpanFor(first, span.marker);
+          if (!found) {
+            span.from = offset;
+            if (sameLine) (first || (first = [])).push(span);
+          }
+        } else {
+          span.from += offset;
+          if (sameLine) (first || (first = [])).push(span);
+        }
+      }
+    }
+
+    var newMarkers = [first];
+    if (!sameLine) {
+      // Fill gap with whole-line-spans
+      var gap = change.text.length - 2, gapMarkers;
+      if (gap > 0 && first)
+        for (var i = 0; i < first.length; ++i)
+          if (first[i].to == null)
+            (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
+      for (var i = 0; i < gap; ++i)
+        newMarkers.push(gapMarkers);
+      newMarkers.push(last);
+    }
+    return newMarkers;
+  }
+
+  function mergeOldSpans(doc, change) {
+    var old = getOldSpans(doc, change);
+    var stretched = stretchSpansOverChange(doc, change);
+    if (!old) return stretched;
+    if (!stretched) return old;
+
+    for (var i = 0; i < old.length; ++i) {
+      var oldCur = old[i], stretchCur = stretched[i];
+      if (oldCur && stretchCur) {
+        spans: for (var j = 0; j < stretchCur.length; ++j) {
+          var span = stretchCur[j];
+          for (var k = 0; k < oldCur.length; ++k)
+            if (oldCur[k].marker == span.marker) continue spans;
+          oldCur.push(span);
+        }
+      } else if (stretchCur) {
+        old[i] = stretchCur;
+      }
+    }
+    return old;
+  }
+
+  function removeReadOnlyRanges(doc, from, to) {
+    var markers = null;
+    doc.iter(from.line, to.line + 1, function(line) {
+      if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
+        var mark = line.markedSpans[i].marker;
+        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
+          (markers || (markers = [])).push(mark);
+      }
+    });
+    if (!markers) return null;
+    var parts = [{from: from, to: to}];
+    for (var i = 0; i < markers.length; ++i) {
+      var mk = markers[i], m = mk.find();
+      for (var j = 0; j < parts.length; ++j) {
+        var p = parts[j];
+        if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
+        var newParts = [j, 1];
+        if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
+          newParts.push({from: p.from, to: m.from});
+        if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
+          newParts.push({from: m.to, to: p.to});
+        parts.splice.apply(parts, newParts);
+        j += newParts.length - 1;
+      }
+    }
+    return parts;
+  }
+
+  function collapsedSpanAt(line, ch) {
+    var sps = sawCollapsedSpans && line.markedSpans, found;
+    if (sps) for (var sp, i = 0; i < sps.length; ++i) {
+      sp = sps[i];
+      if (!sp.marker.collapsed) continue;
+      if ((sp.from == null || sp.from < ch) &&
+          (sp.to == null || sp.to > ch) &&
+          (!found || found.width < sp.marker.width))
+        found = sp.marker;
+    }
+    return found;
+  }
+  function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
+  function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
+
+  function visualLine(doc, line) {
+    var merged;
+    while (merged = collapsedSpanAtStart(line))
+      line = getLine(doc, merged.find().from.line);
+    return line;
+  }
+
+  function lineIsHidden(doc, line) {
+    var sps = sawCollapsedSpans && line.markedSpans;
+    if (sps) for (var sp, i = 0; i < sps.length; ++i) {
+      sp = sps[i];
+      if (!sp.marker.collapsed) continue;
+      if (sp.from == null) return true;
+      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
+        return true;
+    }
+  }
+  function lineIsHiddenInner(doc, line, span) {
+    if (span.to == null) {
+      var end = span.marker.find().to, endLine = getLine(doc, end.line);
+      return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
+    }
+    if (span.marker.inclusiveRight && span.to == line.text.length)
+      return true;
+    for (var sp, i = 0; i < line.markedSpans.length; ++i) {
+      sp = line.markedSpans[i];
+      if (sp.marker.collapsed && sp.from == span.to &&
+          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
+          lineIsHiddenInner(doc, line, sp)) return true;
+    }
+  }
+
+  function detachMarkedSpans(line) {
+    var spans = line.markedSpans;
+    if (!spans) return;
+    for (var i = 0; i < spans.length; ++i)
+      spans[i].marker.detachLine(line);
+    line.markedSpans = null;
+  }
+
+  function attachMarkedSpans(line, spans) {
+    if (!spans) return;
+    for (var i = 0; i < spans.length; ++i)
+      spans[i].marker.attachLine(line);
+    line.markedSpans = spans;
+  }
+
+  // LINE WIDGETS
+
+  var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
+    for (var opt in options) if (options.hasOwnProperty(opt))
+      this[opt] = options[opt];
+    this.cm = cm;
+    this.node = node;
+  };
+  function widgetOperation(f) {
+    return function() {
+      var withOp = !this.cm.curOp;
+      if (withOp) startOperation(this.cm);
+      try {var result = f.apply(this, arguments);}
+      finally {if (withOp) endOperation(this.cm);}
+      return result;
+    };
+  }
+  LineWidget.prototype.clear = widgetOperation(function() {
+    var ws = this.line.widgets, no = lineNo(this.line);
+    if (no == null || !ws) return;
+    for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
+    if (!ws.length) this.line.widgets = null;
+    updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
+    regChange(this.cm, no, no + 1);
+  });
+  LineWidget.prototype.changed = widgetOperation(function() {
+    var oldH = this.height;
+    this.height = null;
+    var diff = widgetHeight(this) - oldH;
+    if (!diff) return;
+    updateLineHeight(this.line, this.line.height + diff);
+    var no = lineNo(this.line);
+    regChange(this.cm, no, no + 1);
+  });
+
+  function widgetHeight(widget) {
+    if (widget.height != null) return widget.height;
+    if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
+      removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
+    return widget.height = widget.node.offsetHeight;
+  }
+
+  function addLineWidget(cm, handle, node, options) {
+    var widget = new LineWidget(cm, node, options);
+    if (widget.noHScroll) cm.display.alignWidgets = true;
+    changeLine(cm, handle, function(line) {
+      (line.widgets || (line.widgets = [])).push(widget);
+      widget.line = line;
+      if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
+        var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
+        updateLineHeight(line, line.height + widgetHeight(widget));
+        if (aboveVisible) addToScrollPos(cm, 0, widget.height);
+      }
+      return true;
+    });
+    return widget;
+  }
+
+  // LINE DATA STRUCTURE
+
+  // Line objects. These hold state related to a line, including
+  // highlighting info (the styles array).
+  function makeLine(text, markedSpans, estimateHeight) {
+    var line = {text: text};
+    attachMarkedSpans(line, markedSpans);
+    line.height = estimateHeight ? estimateHeight(line) : 1;
+    return line;
+  }
+
+  function updateLine(line, text, markedSpans, estimateHeight) {
+    line.text = text;
+    if (line.stateAfter) line.stateAfter = null;
+    if (line.styles) line.styles = null;
+    if (line.order != null) line.order = null;
+    detachMarkedSpans(line);
+    attachMarkedSpans(line, markedSpans);
+    var estHeight = estimateHeight ? estimateHeight(line) : 1;
+    if (estHeight != line.height) updateLineHeight(line, estHeight);
+  }
+
+  function cleanUpLine(line) {
+    line.parent = null;
+    detachMarkedSpans(line);
+  }
+
+  // Run the given mode's parser over a line, update the styles
+  // array, which contains alternating fragments of text and CSS
+  // classes.
+  function runMode(cm, text, mode, state, f) {
+    var flattenSpans = mode.flattenSpans;
+    if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
+    var curText = "", curStyle = null;
+    var stream = new StringStream(text, cm.options.tabSize), style;
+    if (text == "" && mode.blankLine) mode.blankLine(state);
+    while (!stream.eol()) {
+      if (stream.pos > cm.options.maxHighlightLength) {
+        flattenSpans = false;
+        // Webkit seems to refuse to render text nodes longer than 57444 characters
+        stream.pos = Math.min(text.length, stream.start + 50000);
+        style = null;
+      } else {
+        style = mode.token(stream, state);
+      }
+      var substr = stream.current();
+      stream.start = stream.pos;
+      if (!flattenSpans || curStyle != style) {
+        if (curText) f(curText, curStyle);
+        curText = substr; curStyle = style;
+      } else curText = curText + substr;
+    }
+    if (curText) f(curText, curStyle);
+  }
+
+  function highlightLine(cm, line, state) {
+    // A styles array always starts with a number identifying the
+    // mode/overlays that it is based on (for easy invalidation).
+    var st = [cm.state.modeGen];
+    // Compute the base array of styles
+    runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
+
+    // Run overlays, adjust style array.
+    for (var o = 0; o < cm.state.overlays.length; ++o) {
+      var overlay = cm.state.overlays[o], i = 1;
+      runMode(cm, line.text, overlay.mode, true, function(txt, style) {
+        var start = i, len = txt.length;
+        // Ensure there's a token end at the current position, and that i points at it
+        while (len) {
+          var cur = st[i], len_ = cur.length;
+          if (len_ <= len) {
+            len -= len_;
+          } else {
+            st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
+            len = 0;
+          }
+          i += 2;
+        }
+        if (!style) return;
+        if (overlay.opaque) {
+          st.splice(start, i - start, txt, style);
+          i = start + 2;
+        } else {
+          for (; start < i; start += 2) {
+            var cur = st[start+1];
+            st[start+1] = cur ? cur + " " + style : style;
+          }
+        }
+      });
+    }
+
+    return st;
+  }
+
+  function getLineStyles(cm, line) {
+    if (!line.styles || line.styles[0] != cm.state.modeGen)
+      line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
+    return line.styles;
+  }
+
+  // Lightweight form of highlight -- proceed over this line and
+  // update state, but don't save a style array.
+  function processLine(cm, line, state) {
+    var mode = cm.doc.mode;
+    var stream = new StringStream(line.text, cm.options.tabSize);
+    if (line.text == "" && mode.blankLine) mode.blankLine(state);
+    while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
+      mode.token(stream, state);
+      stream.start = stream.pos;
+    }
+  }
+
+  var styleToClassCache = {};
+  function styleToClass(style) {
+    if (!style) return null;
+    return styleToClassCache[style] ||
+      (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
+  }
+
+  function lineContent(cm, realLine, measure) {
+    var merged, line = realLine, lineBefore, sawBefore, simple = true;
+    while (merged = collapsedSpanAtStart(line)) {
+      simple = false;
+      line = getLine(cm.doc, merged.find().from.line);
+      if (!lineBefore) lineBefore = line;
+    }
+
+    var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
+                   measure: null, addedOne: false, cm: cm};
+    if (line.textClass) builder.pre.className = line.textClass;
+
+    do {
+      builder.measure = line == realLine && measure;
+      builder.pos = 0;
+      builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
+      if ((ie || webkit) && cm.getOption("lineWrapping"))
+        builder.addToken = buildTokenSplitSpaces(builder.addToken);
+      if (measure && sawBefore && line != realLine && !builder.addedOne) {
+        measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
+        builder.addedOne = true;
+      }
+      var next = insertLineContent(line, builder, getLineStyles(cm, line));
+      sawBefore = line == lineBefore;
+      if (next) {
+        line = getLine(cm.doc, next.to.line);
+        simple = false;
+      }
+    } while (next);
+
+    if (measure && !builder.addedOne)
+      measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
+    if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
+      builder.pre.appendChild(document.createTextNode("\u00a0"));
+
+    var order;
+    // Work around problem with the reported dimensions of single-char
+    // direction spans on IE (issue #1129). See also the comment in
+    // cursorCoords.
+    if (measure && ie && (order = getOrder(line))) {
+      var l = order.length - 1;
+      if (order[l].from == order[l].to) --l;
+      var last = order[l], prev = order[l - 1];
+      if (last.from + 1 == last.to && prev && last.level < prev.level) {
+        var span = measure[builder.pos - 1];
+        if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
+                                               span.nextSibling);
+      }
+    }
+
+    signal(cm, "renderLine", cm, realLine, builder.pre);
+    return builder.pre;
+  }
+
+  var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
+  function buildToken(builder, text, style, startStyle, endStyle) {
+    if (!text) return;
+    if (!tokenSpecialChars.test(text)) {
+      builder.col += text.length;
+      var content = document.createTextNode(text);
+    } else {
+      var content = document.createDocumentFragment(), pos = 0;
+      while (true) {
+        tokenSpecialChars.lastIndex = pos;
+        var m = tokenSpecialChars.exec(text);
+        var skipped = m ? m.index - pos : text.length - pos;
+        if (skipped) {
+          content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
+          builder.col += skipped;
+        }
+        if (!m) break;
+        pos += skipped + 1;
+        if (m[0] == "\t") {
+          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
+          content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
+          builder.col += tabWidth;
+        } else {
+          var token = elt("span", "\u2022", "cm-invalidchar");
+          token.title = "\\u" + m[0].charCodeAt(0).toString(16);
+          content.appendChild(token);
+          builder.col += 1;
+        }
+      }
+    }
+    if (style || startStyle || endStyle || builder.measure) {
+      var fullStyle = style || "";
+      if (startStyle) fullStyle += startStyle;
+      if (endStyle) fullStyle += endStyle;
+      return builder.pre.appendChild(elt("span", [content], fullStyle));
+    }
+    builder.pre.appendChild(content);
+  }
+
+  function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
+    var wrapping = builder.cm.options.lineWrapping;
+    for (var i = 0; i < text.length; ++i) {
+      var ch = text.charAt(i), start = i == 0;
+      if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
+        ch = text.slice(i, i + 2);
+        ++i;
+      } else if (i && wrapping &&
+                 spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
+        builder.pre.appendChild(elt("wbr"));
+      }
+      var span = builder.measure[builder.pos] =
+        buildToken(builder, ch, style,
+                   start && startStyle, i == text.length - 1 && endStyle);
+      // In IE single-space nodes wrap differently than spaces
+      // embedded in larger text nodes, except when set to
+      // white-space: normal (issue #1268).
+      if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
+          i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
+        span.style.whiteSpace = "normal";
+      builder.pos += ch.length;
+    }
+    if (text.length) builder.addedOne = true;
+  }
+
+  function buildTokenSplitSpaces(inner) {
+    function split(old) {
+      var out = " ";
+      for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
+      out += " ";
+      return out;
+    }
+    return function(builder, text, style, startStyle, endStyle) {
+      return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
+    };
+  }
+
+  function buildCollapsedSpan(builder, size, widget) {
+    if (widget) {
+      if (!builder.display) widget = widget.cloneNode(true);
+      builder.pre.appendChild(widget);
+      if (builder.measure && size) {
+        builder.measure[builder.pos] = widget;
+        builder.addedOne = true;
+      }
+    }
+    builder.pos += size;
+  }
+
+  // Outputs a number of spans to make up a line, taking highlighting
+  // and marked text into account.
+  function insertLineContent(line, builder, styles) {
+    var spans = line.markedSpans;
+    if (!spans) {
+      for (var i = 1; i < styles.length; i+=2)
+        builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
+      return;
+    }
+
+    var allText = line.text, len = allText.length;
+    var pos = 0, i = 1, text = "", style;
+    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
+    for (;;) {
+      if (nextChange == pos) { // Update current marker set
+        spanStyle = spanEndStyle = spanStartStyle = "";
+        collapsed = null; nextChange = Infinity;
+        var foundBookmark = null;
+        for (var j = 0; j < spans.length; ++j) {
+          var sp = spans[j], m = sp.marker;
+          if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
+            if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
+            if (m.className) spanStyle += " " + m.className;
+            if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
+            if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
+            if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
+              collapsed = sp;
+          } else if (sp.from > pos && nextChange > sp.from) {
+            nextChange = sp.from;
+          }
+          if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
+            foundBookmark = m.replacedWith;
+        }
+        if (collapsed && (collapsed.from || 0) == pos) {
+          buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
+                             collapsed.from != null && collapsed.marker.replacedWith);
+          if (collapsed.to == null) return collapsed.marker.find();
+        }
+        if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
+      }
+      if (pos >= len) break;
+
+      var upto = Math.min(len, nextChange);
+      while (true) {
+        if (text) {
+          var end = pos + text.length;
+          if (!collapsed) {
+            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
+            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
+                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
+          }
+          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
+          pos = end;
+          spanStartStyle = "";
+        }
+        text = styles[i++]; style = styleToClass(styles[i++]);
+      }
+    }
+  }
+
+  // DOCUMENT DATA STRUCTURE
+
+  function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
+    function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
+    function update(line, text, spans) {
+      updateLine(line, text, spans, estimateHeight);
+      signalLater(line, "change", line, change);
+    }
+
+    var from = change.from, to = change.to, text = change.text;
+    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
+    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
+
+    // First adjust the line structure
+    if (from.ch == 0 && to.ch == 0 && lastText == "") {
+      // This is a whole-line replace. Treated specially to make
+      // sure line objects move the way they are supposed to.
+      for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
+        added.push(makeLine(text[i], spansFor(i), estimateHeight));
+      update(lastLine, lastLine.text, lastSpans);
+      if (nlines) doc.remove(from.line, nlines);
+      if (added.length) doc.insert(from.line, added);
+    } else if (firstLine == lastLine) {
+      if (text.length == 1) {
+        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
+      } else {
+        for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
+          added.push(makeLine(text[i], spansFor(i), estimateHeight));
+        added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
+        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
+        doc.insert(from.line + 1, added);
+      }
+    } else if (text.length == 1) {
+      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
+      doc.remove(from.line + 1, nlines);
+    } else {
+      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
+      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
+      for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
+        added.push(makeLine(text[i], spansFor(i), estimateHeight));
+      if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
+      doc.insert(from.line + 1, added);
+    }
+
+    signalLater(doc, "change", doc, change);
+    setSelection(doc, selAfter.anchor, selAfter.head, null, true);
+  }
+
+  function LeafChunk(lines) {
+    this.lines = lines;
+    this.parent = null;
+    for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
+      lines[i].parent = this;
+      height += lines[i].height;
+    }
+    this.height = height;
+  }
+
+  LeafChunk.prototype = {
+    chunkSize: function() { return this.lines.length; },
+    removeInner: function(at, n) {
+      for (var i = at, e = at + n; i < e; ++i) {
+        var line = this.lines[i];
+        this.height -= line.height;
+        cleanUpLine(line);
+        signalLater(line, "delete");
+      }
+      this.lines.splice(at, n);
+    },
+    collapse: function(lines) {
+      lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
+    },
+    insertInner: function(at, lines, height) {
+      this.height += height;
+      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
+      for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
+    },
+    iterN: function(at, n, op) {
+      for (var e = at + n; at < e; ++at)
+        if (op(this.lines[at])) return true;
+    }
+  };
+
+  function BranchChunk(children) {
+    this.children = children;
+    var size = 0, height = 0;
+    for (var i = 0, e = children.length; i < e; ++i) {
+      var ch = children[i];
+      size += ch.chunkSize(); height += ch.height;
+      ch.parent = this;
+    }
+    this.size = size;
+    this.height = height;
+    this.parent = null;
+  }
+
+  BranchChunk.prototype = {
+    chunkSize: function() { return this.size; },
+    removeInner: function(at, n) {
+      this.size -= n;
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i], sz = child.chunkSize();
+        if (at < sz) {
+          var rm = Math.min(n, sz - at), oldHeight = child.height;
+          child.removeInner(at, rm);
+          this.height -= oldHeight - child.height;
+          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
+          if ((n -= rm) == 0) break;
+          at = 0;
+        } else at -= sz;
+      }
+      if (this.size - n < 25) {
+        var lines = [];
+        this.collapse(lines);
+        this.children = [new LeafChunk(lines)];
+        this.children[0].parent = this;
+      }
+    },
+    collapse: function(lines) {
+      for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
+    },
+    insertInner: function(at, lines, height) {
+      this.size += lines.length;
+      this.height += height;
+      for (var i = 0, e = this.children.length; i < e; ++i) {
+        var child = this.children[i], sz = child.chunkSize();
+        if (at <= sz) {
+          child.insertInner(at, lines, height);
+          if (child.lines && child.lines.length > 50) {
+            while (child.lines.length > 50) {
+              var spilled = child.lines.splice(child.lines.length - 25, 25);
+              var newleaf = new LeafChunk(spilled);
+              child.height -= newleaf.height;
+              this.children.splice(i + 1, 0, newleaf);
+              newleaf.parent = this;
+            }
+            this.maybeSpill();
+          }
+          break;
+        }
+        at -= sz;
+      }
+    },
+    maybeSpill: function() {
+      if (this.children.length <= 10) return;
+      var me = this;
+      do {
+        var spilled = me.children.splice(me.children.length - 5, 5);
+        var sibling = new BranchChunk(spilled);
+        if (!me.parent) { // Become the parent node
+          var copy = new BranchChunk(me.children);
+          copy.parent = me;
+          me.children = [copy, sibling];
+          me = copy;
+        } else {
+          me.size -= sibling.size;
+          me.height -= sibling.height;
+          var myIndex = indexOf(me.parent.children, me);
+          me.parent.children.splice(myIndex + 1, 0, sibling);
+        }
+        sibling.parent = me.parent;
+      } while (me.children.length > 10);
+      me.parent.maybeSpill();
+    },
+    iterN: function(at, n, op) {
+      for (var i = 0, e = this.children.length; i < e; ++i) {
+        var child = this.children[i], sz = child.chunkSize();
+        if (at < sz) {
+          var used = Math.min(n, sz - at);
+          if (child.iterN(at, used, op)) return true;
+          if ((n -= used) == 0) break;
+          at = 0;
+        } else at -= sz;
+      }
+    }
+  };
+
+  var nextDocId = 0;
+  var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
+    if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
+    if (firstLine == null) firstLine = 0;
+
+    BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
+    this.first = firstLine;
+    this.scrollTop = this.scrollLeft = 0;
+    this.cantEdit = false;
+    this.history = makeHistory();
+    this.frontier = firstLine;
+    var start = Pos(firstLine, 0);
+    this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
+    this.id = ++nextDocId;
+    this.modeOption = mode;
+
+    if (typeof text == "string") text = splitLines(text);
+    updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
+  };
+
+  Doc.prototype = createObj(BranchChunk.prototype, {
+    iter: function(from, to, op) {
+      if (op) this.iterN(from - this.first, to - from, op);
+      else this.iterN(this.first, this.first + this.size, from);
+    },
+
+    insert: function(at, lines) {
+      var height = 0;
+      for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
+      this.insertInner(at - this.first, lines, height);
+    },
+    remove: function(at, n) { this.removeInner(at - this.first, n); },
+
+    getValue: function(lineSep) {
+      var lines = getLines(this, this.first, this.first + this.size);
+      if (lineSep === false) return lines;
+      return lines.join(lineSep || "\n");
+    },
+    setValue: function(code) {
+      var top = Pos(this.first, 0), last = this.first + this.size - 1;
+      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
+                        text: splitLines(code), origin: "setValue"},
+                 {head: top, anchor: top}, true);
+    },
+    replaceRange: function(code, from, to, origin) {
+      from = clipPos(this, from);
+      to = to ? clipPos(this, to) : from;
+      replaceRange(this, code, from, to, origin);
+    },
+    getRange: function(from, to, lineSep) {
+      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
+      if (lineSep === false) return lines;
+      return lines.join(lineSep || "\n");
+    },
+
+    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
+    setLine: function(line, text) {
+      if (isLine(this, line))
+        replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
+    },
+    removeLine: function(line) {
+      if (line) replaceRange(this, "", clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
+      else replaceRange(this, "", Pos(0, 0), clipPos(this, Pos(1, 0)));
+    },
+
+    getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
+    getLineNumber: function(line) {return lineNo(line);},
+
+    lineCount: function() {return this.size;},
+    firstLine: function() {return this.first;},
+    lastLine: function() {return this.first + this.size - 1;},
+
+    clipPos: function(pos) {return clipPos(this, pos);},
+
+    getCursor: function(start) {
+      var sel = this.sel, pos;
+      if (start == null || start == "head") pos = sel.head;
+      else if (start == "anchor") pos = sel.anchor;
+      else if (start == "end" || start === false) pos = sel.to;
+      else pos = sel.from;
+      return copyPos(pos);
+    },
+    somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
+
+    setCursor: docOperation(function(line, ch, extend) {
+      var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
+      if (extend) extendSelection(this, pos);
+      else setSelection(this, pos, pos);
+    }),
+    setSelection: docOperation(function(anchor, head) {
+      setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
+    }),
+    extendSelection: docOperation(function(from, to) {
+      extendSelection(this, clipPos(this, from), to && clipPos(this, to));
+    }),
+
+    getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
+    replaceSelection: function(code, collapse, origin) {
+      makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
+    },
+    undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
+    redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
+
+    setExtending: function(val) {this.sel.extend = val;},
+
+    historySize: function() {
+      var hist = this.history;
+      return {undo: hist.done.length, redo: hist.undone.length};
+    },
+    clearHistory: function() {this.history = makeHistory();},
+
+    markClean: function() {
+      this.history.dirtyCounter = 0;
+      this.history.lastOp = this.history.lastOrigin = null;
+    },
+    isClean: function () {return this.history.dirtyCounter == 0;},
+
+    getHistory: function() {
+      return {done: copyHistoryArray(this.history.done),
+              undone: copyHistoryArray(this.history.undone)};
+    },
+    setHistory: function(histData) {
+      var hist = this.history = makeHistory();
+      hist.done = histData.done.slice(0);
+      hist.undone = histData.undone.slice(0);
+    },
+
+    markText: function(from, to, options) {
+      return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
+    },
+    setBookmark: function(pos, options) {
+      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
+                      insertLeft: options && options.insertLeft};
+      pos = clipPos(this, pos);
+      return markText(this, pos, pos, realOpts, "bookmark");
+    },
+    findMarksAt: function(pos) {
+      pos = clipPos(this, pos);
+      var markers = [], spans = getLine(this, pos.line).markedSpans;
+      if (spans) for (var i = 0; i < spans.length; ++i) {
+        var span = spans[i];
+        if ((span.from == null || span.from <= pos.ch) &&
+            (span.to == null || span.to >= pos.ch))
+          markers.push(span.marker.parent || span.marker);
+      }
+      return markers;
+    },
+    getAllMarks: function() {
+      var markers = [];
+      this.iter(function(line) {
+        var sps = line.markedSpans;
+        if (sps) for (var i = 0; i < sps.length; ++i)
+          if (sps[i].from != null) markers.push(sps[i].marker);
+      });
+      return markers;
+    },
+
+    posFromIndex: function(off) {
+      var ch, lineNo = this.first;
+      this.iter(function(line) {
+        var sz = line.text.length + 1;
+        if (sz > off) { ch = off; return true; }
+        off -= sz;
+        ++lineNo;
+      });
+      return clipPos(this, Pos(lineNo, ch));
+    },
+    indexFromPos: function (coords) {
+      coords = clipPos(this, coords);
+      var index = coords.ch;
+      if (coords.line < this.first || coords.ch < 0) return 0;
+      this.iter(this.first, coords.line, function (line) {
+        index += line.text.length + 1;
+      });
+      return index;
+    },
+
+    copy: function(copyHistory) {
+      var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
+      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
+      doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
+                 shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
+      if (copyHistory) {
+        doc.history.undoDepth = this.history.undoDepth;
+        doc.setHistory(this.getHistory());
+      }
+      return doc;
+    },
+
+    linkedDoc: function(options) {
+      if (!options) options = {};
+      var from = this.first, to = this.first + this.size;
+      if (options.from != null && options.from > from) from = options.from;
+      if (options.to != null && options.to < to) to = options.to;
+      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
+      if (options.sharedHist) copy.history = this.history;
+      (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
+      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
+      return copy;
+    },
+    unlinkDoc: function(other) {
+      if (other instanceof CodeMirror) other = other.doc;
+      if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
+        var link = this.linked[i];
+        if (link.doc != other) continue;
+        this.linked.splice(i, 1);
+        other.unlinkDoc(this);
+        break;
+      }
+      // If the histories were shared, split them again
+      if (other.history == this.history) {
+        var splitIds = [other.id];
+        linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
+        other.history = makeHistory();
+        other.history.done = copyHistoryArray(this.history.done, splitIds);
+        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
+      }
+    },
+    iterLinkedDocs: function(f) {linkedDocs(this, f);},
+
+    getMode: function() {return this.mode;},
+    getEditor: function() {return this.cm;}
+  });
+
+  Doc.prototype.eachLine = Doc.prototype.iter;
+
+  // The Doc methods that should be available on CodeMirror instances
+  var dontDelegate = "iter insert remove copy getEditor".split(" ");
+  for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
+    CodeMirror.prototype[prop] = (function(method) {
+      return function() {return method.apply(this.doc, arguments);};
+    })(Doc.prototype[prop]);
+
+  function linkedDocs(doc, f, sharedHistOnly) {
+    function propagate(doc, skip, sharedHist) {
+      if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
+        var rel = doc.linked[i];
+        if (rel.doc == skip) continue;
+        var shared = sharedHist && rel.sharedHist;
+        if (sharedHistOnly && !shared) continue;
+        f(rel.doc, shared);
+        propagate(rel.doc, doc, shared);
+      }
+    }
+    propagate(doc, null, true);
+  }
+
+  function attachDoc(cm, doc) {
+    if (doc.cm) throw new Error("This document is already in use.");
+    cm.doc = doc;
+    doc.cm = cm;
+    estimateLineHeights(cm);
+    loadMode(cm);
+    if (!cm.options.lineWrapping) computeMaxLength(cm);
+    cm.options.mode = doc.modeOption;
+    regChange(cm);
+  }
+
+  // LINE UTILITIES
+
+  function getLine(chunk, n) {
+    n -= chunk.first;
+    while (!chunk.lines) {
+      for (var i = 0;; ++i) {
+        var child = chunk.children[i], sz = child.chunkSize();
+        if (n < sz) { chunk = child; break; }
+        n -= sz;
+      }
+    }
+    return chunk.lines[n];
+  }
+
+  function getBetween(doc, start, end) {
+    var out = [], n = start.line;
+    doc.iter(start.line, end.line + 1, function(line) {
+      var text = line.text;
+      if (n == end.line) text = text.slice(0, end.ch);
+      if (n == start.line) text = text.slice(start.ch);
+      out.push(text);
+      ++n;
+    });
+    return out;
+  }
+  function getLines(doc, from, to) {
+    var out = [];
+    doc.iter(from, to, function(line) { out.push(line.text); });
+    return out;
+  }
+
+  function updateLineHeight(line, height) {
+    var diff = height - line.height;
+    for (var n = line; n; n = n.parent) n.height += diff;
+  }
+
+  function lineNo(line) {
+    if (line.parent == null) return null;
+    var cur = line.parent, no = indexOf(cur.lines, line);
+    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
+      for (var i = 0;; ++i) {
+        if (chunk.children[i] == cur) break;
+        no += chunk.children[i].chunkSize();
+      }
+    }
+    return no + cur.first;
+  }
+
+  function lineAtHeight(chunk, h) {
+    var n = chunk.first;
+    outer: do {
+      for (var i = 0, e = chunk.children.length; i < e; ++i) {
+        var child = chunk.children[i], ch = child.height;
+        if (h < ch) { chunk = child; continue outer; }
+        h -= ch;
+        n += child.chunkSize();
+      }
+      return n;
+    } while (!chunk.lines);
+    for (var i = 0, e = chunk.lines.length; i < e; ++i) {
+      var line = chunk.lines[i], lh = line.height;
+      if (h < lh) break;
+      h -= lh;
+    }
+    return n + i;
+  }
+
+  function heightAtLine(cm, lineObj) {
+    lineObj = visualLine(cm.doc, lineObj);
+
+    var h = 0, chunk = lineObj.parent;
+    for (var i = 0; i < chunk.lines.length; ++i) {
+      var line = chunk.lines[i];
+      if (line == lineObj) break;
+      else h += line.height;
+    }
+    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
+      for (var i = 0; i < p.children.length; ++i) {
+        var cur = p.children[i];
+        if (cur == chunk) break;
+        else h += cur.height;
+      }
+    }
+    return h;
+  }
+
+  function getOrder(line) {
+    var order = line.order;
+    if (order == null) order = line.order = bidiOrdering(line.text);
+    return order;
+  }
+
+  // HISTORY
+
+  function makeHistory() {
+    return {
+      // Arrays of history events. Doing something adds an event to
+      // done and clears undo. Undoing moves events from done to
+      // undone, redoing moves them in the other direction.
+      done: [], undone: [], undoDepth: Infinity,
+      // Used to track when changes can be merged into a single undo
+      // event
+      lastTime: 0, lastOp: null, lastOrigin: null,
+      // Used by the isClean() method
+      dirtyCounter: 0
+    };
+  }
+
+  function attachLocalSpans(doc, change, from, to) {
+    var existing = change["spans_" + doc.id], n = 0;
+    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
+      if (line.markedSpans)
+        (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
+      ++n;
+    });
+  }
+
+  function historyChangeFromChange(doc, change) {
+    var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
+    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
+    linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
+    return histChange;
+  }
+
+  function addToHistory(doc, change, selAfter, opId) {
+    var hist = doc.history;
+    hist.undone.length = 0;
+    var time = +new Date, cur = lst(hist.done);
+
+    if (cur &&
+        (hist.lastOp == opId ||
+         hist.lastOrigin == change.origin && change.origin &&
+         ((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) ||
+          change.origin.charAt(0) == "*"))) {
+      // Merge this change into the last event
+      var last = lst(cur.changes);
+      if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
+        // Optimized case for simple insertion -- don't want to add
+        // new changesets for every character typed
+        last.to = changeEnd(change);
+      } else {
+        // Add new sub-event
+        cur.changes.push(historyChangeFromChange(doc, change));
+      }
+      cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
+    } else {
+      // Can not be merged, start a new event.
+      cur = {changes: [historyChangeFromChange(doc, change)],
+             anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
+             anchorAfter: selAfter.anchor, headAfter: selAfter.head};
+      hist.done.push(cur);
+      while (hist.done.length > hist.undoDepth)
+        hist.done.shift();
+      if (hist.dirtyCounter < 0)
+        // The user has made a change after undoing past the last clean state.
+        // We can never get back to a clean state now until markClean() is called.
+        hist.dirtyCounter = NaN;
+      else
+        hist.dirtyCounter++;
+    }
+    hist.lastTime = time;
+    hist.lastOp = opId;
+    hist.lastOrigin = change.origin;
+  }
+
+  function removeClearedSpans(spans) {
+    if (!spans) return null;
+    for (var i = 0, out; i < spans.length; ++i) {
+      if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
+      else if (out) out.push(spans[i]);
+    }
+    return !out ? spans : out.length ? out : null;
+  }
+
+  function getOldSpans(doc, change) {
+    var found = change["spans_" + doc.id];
+    if (!found) return null;
+    for (var i = 0, nw = []; i < change.text.length; ++i)
+      nw.push(removeClearedSpans(found[i]));
+    return nw;
+  }
+
+  // Used both to provide a JSON-safe object in .getHistory, and, when
+  // detaching a document, to split the history in two
+  function copyHistoryArray(events, newGroup) {
+    for (var i = 0, copy = []; i < events.length; ++i) {
+      var event = events[i], changes = event.changes, newChanges = [];
+      copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
+                 anchorAfter: event.anchorAfter, headAfter: event.headAfter});
+      for (var j = 0; j < changes.length; ++j) {
+        var change = changes[j], m;
+        newChanges.push({from: change.from, to: change.to, text: change.text});
+        if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
+          if (indexOf(newGroup, Number(m[1])) > -1) {
+            lst(newChanges)[prop] = change[prop];
+            delete change[prop];
+          }
+        }
+      }
+    }
+    return copy;
+  }
+
+  // Rebasing/resetting history to deal with externally-sourced changes
+
+  function rebaseHistSel(pos, from, to, diff) {
+    if (to < pos.line) {
+      pos.line += diff;
+    } else if (from < pos.line) {
+      pos.line = from;
+      pos.ch = 0;
+    }
+  }
+
+  // Tries to rebase an array of history events given a change in the
+  // document. If the change touches the same lines as the event, the
+  // event, and everything 'behind' it, is discarded. If the change is
+  // before the event, the event's positions are updated. Uses a
+  // copy-on-write scheme for the positions, to avoid having to
+  // reallocate them all on every rebase, but also avoid problems with
+  // shared position objects being unsafely updated.
+  function rebaseHistArray(array, from, to, diff) {
+    for (var i = 0; i < array.length; ++i) {
+      var sub = array[i], ok = true;
+      for (var j = 0; j < sub.changes.length; ++j) {
+        var cur = sub.changes[j];
+        if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
+        if (to < cur.from.line) {
+          cur.from.line += diff;
+          cur.to.line += diff;
+        } else if (from <= cur.to.line) {
+          ok = false;
+          break;
+        }
+      }
+      if (!sub.copied) {
+        sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
+        sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
+        sub.copied = true;
+      }
+      if (!ok) {
+        array.splice(0, i + 1);
+        i = 0;
+      } else {
+        rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
+        rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
+      }
+    }
+  }
+
+  function rebaseHist(hist, change) {
+    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
+    rebaseHistArray(hist.done, from, to, diff);
+    rebaseHistArray(hist.undone, from, to, diff);
+  }
+
+  // EVENT OPERATORS
+
+  function stopMethod() {e_stop(this);}
+  // Ensure an event has a stop method.
+  function addStop(event) {
+    if (!event.stop) event.stop = stopMethod;
+    return event;
+  }
+
+  function e_preventDefault(e) {
+    if (e.preventDefault) e.preventDefault();
+    else e.returnValue = false;
+  }
+  function e_stopPropagation(e) {
+    if (e.stopPropagation) e.stopPropagation();
+    else e.cancelBubble = true;
+  }
+  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
+  CodeMirror.e_stop = e_stop;
+  CodeMirror.e_preventDefault = e_preventDefault;
+  CodeMirror.e_stopPropagation = e_stopPropagation;
+
+  function e_target(e) {return e.target || e.srcElement;}
+  function e_button(e) {
+    var b = e.which;
+    if (b == null) {
+      if (e.button & 1) b = 1;
+      else if (e.button & 2) b = 3;
+      else if (e.button & 4) b = 2;
+    }
+    if (mac && e.ctrlKey && b == 1) b = 3;
+    return b;
+  }
+
+  // EVENT HANDLING
+
+  function on(emitter, type, f) {
+    if (emitter.addEventListener)
+      emitter.addEventListener(type, f, false);
+    else if (emitter.attachEvent)
+      emitter.attachEvent("on" + type, f);
+    else {
+      var map = emitter._handlers || (emitter._handlers = {});
+      var arr = map[type] || (map[type] = []);
+      arr.push(f);
+    }
+  }
+
+  function off(emitter, type, f) {
+    if (emitter.removeEventListener)
+      emitter.removeEventListener(type, f, false);
+    else if (emitter.detachEvent)
+      emitter.detachEvent("on" + type, f);
+    else {
+      var arr = emitter._handlers && emitter._handlers[type];
+      if (!arr) return;
+      for (var i = 0; i < arr.length; ++i)
+        if (arr[i] == f) { arr.splice(i, 1); break; }
+    }
+  }
+
+  function signal(emitter, type /*, values...*/) {
+    var arr = emitter._handlers && emitter._handlers[type];
+    if (!arr) return;
+    var args = Array.prototype.slice.call(arguments, 2);
+    for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
+  }
+
+  var delayedCallbacks, delayedCallbackDepth = 0;
+  function signalLater(emitter, type /*, values...*/) {
+    var arr = emitter._handlers && emitter._handlers[type];
+    if (!arr) return;
+    var args = Array.prototype.slice.call(arguments, 2);
+    if (!delayedCallbacks) {
+      ++delayedCallbackDepth;
+      delayedCallbacks = [];
+      setTimeout(fireDelayed, 0);
+    }
+    function bnd(f) {return function(){f.apply(null, args);};};
+    for (var i = 0; i < arr.length; ++i)
+      delayedCallbacks.push(bnd(arr[i]));
+  }
+
+  function fireDelayed() {
+    --delayedCallbackDepth;
+    var delayed = delayedCallbacks;
+    delayedCallbacks = null;
+    for (var i = 0; i < delayed.length; ++i) delayed[i]();
+  }
+
+  function hasHandler(emitter, type) {
+    var arr = emitter._handlers && emitter._handlers[type];
+    return arr && arr.length > 0;
+  }
+
+  CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
+
+  // MISC UTILITIES
+
+  // Number of pixels added to scroller and sizer to hide scrollbar
+  var scrollerCutOff = 30;
+
+  // Returned or thrown by various protocols to signal 'I'm not
+  // handling this'.
+  var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
+
+  function Delayed() {this.id = null;}
+  Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
+
+  // Counts the column offset in a string, taking tabs into account.
+  // Used mostly to find indentation.
+  function countColumn(string, end, tabSize, startIndex, startValue) {
+    if (end == null) {
+      end = string.search(/[^\s\u00a0]/);
+      if (end == -1) end = string.length;
+    }
+    for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
+      if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
+      else ++n;
+    }
+    return n;
+  }
+  CodeMirror.countColumn = countColumn;
+
+  var spaceStrs = [""];
+  function spaceStr(n) {
+    while (spaceStrs.length <= n)
+      spaceStrs.push(lst(spaceStrs) + " ");
+    return spaceStrs[n];
+  }
+
+  function lst(arr) { return arr[arr.length-1]; }
+
+  function selectInput(node) {
+    if (ios) { // Mobile Safari apparently has a bug where select() is broken.
+      node.selectionStart = 0;
+      node.selectionEnd = node.value.length;
+    } else node.select();
+  }
+
+  function indexOf(collection, elt) {
+    if (collection.indexOf) return collection.indexOf(elt);
+    for (var i = 0, e = collection.length; i < e; ++i)
+      if (collection[i] == elt) return i;
+    return -1;
+  }
+
+  function createObj(base, props) {
+    function Obj() {}
+    Obj.prototype = base;
+    var inst = new Obj();
+    if (props) copyObj(props, inst);
+    return inst;
+  }
+
+  function copyObj(obj, target) {
+    if (!target) target = {};
+    for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
+    return target;
+  }
+
+  function emptyArray(size) {
+    for (var a = [], i = 0; i < size; ++i) a.push(undefined);
+    return a;
+  }
+
+  function bind(f) {
+    var args = Array.prototype.slice.call(arguments, 1);
+    return function(){return f.apply(null, args);};
+  }
+
+  var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
+  function isWordChar(ch) {
+    return /\w/.test(ch) || ch > "\x80" &&
+      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
+  }
+
+  function isEmpty(obj) {
+    for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
+    return true;
+  }
+
+  var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
+
+  // DOM UTILITIES
+
+  function elt(tag, content, className, style) {
+    var e = document.createElement(tag);
+    if (className) e.className = className;
+    if (style) e.style.cssText = style;
+    if (typeof content == "string") setTextContent(e, content);
+    else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
+    return e;
+  }
+
+  function removeChildren(e) {
+    for (var count = e.childNodes.length; count > 0; --count)
+      e.removeChild(e.firstChild);
+    return e;
+  }
+
+  function removeChildrenAndAdd(parent, e) {
+    return removeChildren(parent).appendChild(e);
+  }
+
+  function setTextContent(e, str) {
+    if (ie_lt9) {
+      e.innerHTML = "";
+      e.appendChild(document.createTextNode(str));
+    } else e.textContent = str;
+  }
+
+  function getRect(node) {
+    return node.getBoundingClientRect();
+  }
+  CodeMirror.replaceGetRect = function(f) { getRect = f; };
+
+  // FEATURE DETECTION
+
+  // Detect drag-and-drop
+  var dragAndDrop = function() {
+    // There is *some* kind of drag-and-drop support in IE6-8, but I
+    // couldn't get it to work yet.
+    if (ie_lt9) return false;
+    var div = elt('div');
+    return "draggable" in div || "dragDrop" in div;
+  }();
+
+  // For a reason I have yet to figure out, some browsers disallow
+  // word wrapping between certain characters *only* if a new inline
+  // element is started between them. This makes it hard to reliably
+  // measure the position of things, since that requires inserting an
+  // extra span. This terribly fragile set of regexps matches the
+  // character combinations that suffer from this phenomenon on the
+  // various browsers.
+  var spanAffectsWrapping = /^$/; // Won't match any two-character string
+  if (gecko) spanAffectsWrapping = /$'/;
+  else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
+  else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
+
+  var knownScrollbarWidth;
+  function scrollbarWidth(measure) {
+    if (knownScrollbarWidth != null) return knownScrollbarWidth;
+    var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
+    removeChildrenAndAdd(measure, test);
+    if (test.offsetWidth)
+      knownScrollbarWidth = test.offsetHeight - test.clientHeight;
+    return knownScrollbarWidth || 0;
+  }
+
+  var zwspSupported;
+  function zeroWidthElement(measure) {
+    if (zwspSupported == null) {
+      var test = elt("span", "\u200b");
+      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
+      if (measure.firstChild.offsetHeight != 0)
+        zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
+    }
+    if (zwspSupported) return elt("span", "\u200b");
+    else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
+  }
+
+  // See if "".split is the broken IE version, if so, provide an
+  // alternative way to split lines.
+  var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
+    var pos = 0, result = [], l = string.length;
+    while (pos <= l) {
+      var nl = string.indexOf("\n", pos);
+      if (nl == -1) nl = string.length;
+      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
+      var rt = line.indexOf("\r");
+      if (rt != -1) {
+        result.push(line.slice(0, rt));
+        pos += rt + 1;
+      } else {
+        result.push(line);
+        pos = nl + 1;
+      }
+    }
+    return result;
+  } : function(string){return string.split(/\r\n?|\n/);};
+  CodeMirror.splitLines = splitLines;
+
+  var hasSelection = window.getSelection ? function(te) {
+    try { return te.selectionStart != te.selectionEnd; }
+    catch(e) { return false; }
+  } : function(te) {
+    try {var range = te.ownerDocument.selection.createRange();}
+    catch(e) {}
+    if (!range || range.parentElement() != te) return false;
+    return range.compareEndPoints("StartToEnd", range) != 0;
+  };
+
+  var hasCopyEvent = (function() {
+    var e = elt("div");
+    if ("oncopy" in e) return true;
+    e.setAttribute("oncopy", "return;");
+    return typeof e.oncopy == 'function';
+  })();
+
+  // KEY NAMING
+
+  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+                  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
+                  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
+                  46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
+                  186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
+                  221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
+                  63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
+  CodeMirror.keyNames = keyNames;
+  (function() {
+    // Number keys
+    for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
+    // Alphabetic keys
+    for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
+    // Function keys
+    for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
+  })();
+
+  // BIDI HELPERS
+
+  function iterateBidiSections(order, from, to, f) {
+    if (!order) return f(from, to, "ltr");
+    for (var i = 0; i < order.length; ++i) {
+      var part = order[i];
+      if (part.from < to && part.to > from || from == to && part.to == from)
+        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
+    }
+  }
+
+  function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
+  function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
+
+  function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
+  function lineRight(line) {
+    var order = getOrder(line);
+    if (!order) return line.text.length;
+    return bidiRight(lst(order));
+  }
+
+  function lineStart(cm, lineN) {
+    var line = getLine(cm.doc, lineN);
+    var visual = visualLine(cm.doc, line);
+    if (visual != line) lineN = lineNo(visual);
+    var order = getOrder(visual);
+    var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
+    return Pos(lineN, ch);
+  }
+  function lineEnd(cm, lineN) {
+    var merged, line;
+    while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
+      lineN = merged.find().to.line;
+    var order = getOrder(line);
+    var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
+    return Pos(lineN, ch);
+  }
+
+  // This is somewhat involved. It is needed in order to move
+  // 'visually' through bi-directional text -- i.e., pressing left
+  // should make the cursor go left, even when in RTL text. The
+  // tricky part is the 'jumps', where RTL and LTR text touch each
+  // other. This often requires the cursor offset to move more than
+  // one unit, in order to visually move one unit.
+  function moveVisually(line, start, dir, byUnit) {
+    var bidi = getOrder(line);
+    if (!bidi) return moveLogically(line, start, dir, byUnit);
+    var moveOneUnit = byUnit ? function(pos, dir) {
+      do pos += dir;
+      while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
+      return pos;
+    } : function(pos, dir) { return pos + dir; };
+    var linedir = bidi[0].level;
+    for (var i = 0; i < bidi.length; ++i) {
+      var part = bidi[i], sticky = part.level % 2 == linedir;
+      if ((part.from < start && part.to > start) ||
+          (sticky && (part.from == start || part.to == start))) break;
+    }
+    var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
+
+    while (target != null) {
+      if (part.level % 2 == linedir) {
+        if (target < part.from || target > part.to) {
+          part = bidi[i += dir];
+          target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
+        } else break;
+      } else {
+        if (target == bidiLeft(part)) {
+          part = bidi[--i];
+          target = part && bidiRight(part);
+        } else if (target == bidiRight(part)) {
+          part = bidi[++i];
+          target = part && bidiLeft(part);
+        } else break;
+      }
+    }
+
+    return target < 0 || target > line.text.length ? null : target;
+  }
+
+  function moveLogically(line, start, dir, byUnit) {
+    var target = start + dir;
+    if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
+    return target < 0 || target > line.text.length ? null : target;
+  }
+
+  // Bidirectional ordering algorithm
+  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
+  // that this (partially) implements.
+
+  // One-char codes used for character types:
+  // L (L):   Left-to-Right
+  // R (R):   Right-to-Left
+  // r (AL):  Right-to-Left Arabic
+  // 1 (EN):  European Number
+  // + (ES):  European Number Separator
+  // % (ET):  European Number Terminator
+  // n (AN):  Arabic Number
+  // , (CS):  Common Number Separator
+  // m (NSM): Non-Spacing Mark
+  // b (BN):  Boundary Neutral
+  // s (B):   Paragraph Separator
+  // t (S):   Segment Separator
+  // w (WS):  Whitespace
+  // N (ON):  Other Neutrals
+
+  // Returns null if characters are ordered as they appear
+  // (left-to-right), or an array of sections ({from, to, level}
+  // objects) in the order in which they occur visually.
+  var bidiOrdering = (function() {
+    // Character types for codepoints 0 to 0xff
+    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
+    // Character types for codepoints 0x600 to 0x6ff
+    var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
+    function charType(code) {
+      if (code <= 0xff) return lowTypes.charAt(code);
+      else if (0x590 <= code && code <= 0x5f4) return "R";
+      else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
+      else if (0x700 <= code && code <= 0x8ac) return "r";
+      else return "L";
+    }
+
+    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
+    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
+    // Browsers seem to always treat the boundaries of block elements as being L.
+    var outerType = "L";
+
+    return function(str) {
+      if (!bidiRE.test(str)) return false;
+      var len = str.length, types = [];
+      for (var i = 0, type; i < len; ++i)
+        types.push(type = charType(str.charCodeAt(i)));
+
+      // W1. Examine each non-spacing mark (NSM) in the level run, and
+      // change the type of the NSM to the type of the previous
+      // character. If the NSM is at the start of the level run, it will
+      // get the type of sor.
+      for (var i = 0, prev = outerType; i < len; ++i) {
+        var type = types[i];
+        if (type == "m") types[i] = prev;
+        else prev = type;
+      }
+
+      // W2. Search backwards from each instance of a European number
+      // until the first strong type (R, L, AL, or sor) is found. If an
+      // AL is found, change the type of the European number to Arabic
+      // number.
+      // W3. Change all ALs to R.
+      for (var i = 0, cur = outerType; i < len; ++i) {
+        var type = types[i];
+        if (type == "1" && cur == "r") types[i] = "n";
+        else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
+      }
+
+      // W4. A single European separator between two European numbers
+      // changes to a European number. A single common separator between
+      // two numbers of the same type changes to that type.
+      for (var i = 1, prev = types[0]; i < len - 1; ++i) {
+        var type = types[i];
+        if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
+        else if (type == "," && prev == types[i+1] &&
+                 (prev == "1" || prev == "n")) types[i] = prev;
+        prev = type;
+      }
+
+      // W5. A sequence of European terminators adjacent to European
+      // numbers changes to all European numbers.
+      // W6. Otherwise, separators and terminators change to Other
+      // Neutral.
+      for (var i = 0; i < len; ++i) {
+        var type = types[i];
+        if (type == ",") types[i] = "N";
+        else if (type == "%") {
+          for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
+          var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
+          for (var j = i; j < end; ++j) types[j] = replace;
+          i = end - 1;
+        }
+      }
+
+      // W7. Search backwards from each instance of a European number
+      // until the first strong type (R, L, or sor) is found. If an L is
+      // found, then change the type of the European number to L.
+      for (var i = 0, cur = outerType; i < len; ++i) {
+        var type = types[i];
+        if (cur == "L" && type == "1") types[i] = "L";
+        else if (isStrong.test(type)) cur = type;
+      }
+
+      // N1. A sequence of neutrals takes the direction of the
+      // surrounding strong text if the text on both sides has the same
+      // direction. European and Arabic numbers act as if they were R in
+      // terms of their influence on neutrals. Start-of-level-run (sor)
+      // and end-of-level-run (eor) are used at level run boundaries.
+      // N2. Any remaining neutrals take the embedding direction.
+      for (var i = 0; i < len; ++i) {
+        if (isNeutral.test(types[i])) {
+          for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
+          var before = (i ? types[i-1] : outerType) == "L";
+          var after = (end < len - 1 ? types[end] : outerType) == "L";
+          var replace = before || after ? "L" : "R";
+          for (var j = i; j < end; ++j) types[j] = replace;
+          i = end - 1;
+        }
+      }
+
+      // Here we depart from the documented algorithm, in order to avoid
+      // building up an actual levels array. Since there are only three
+      // levels (0, 1, 2) in an implementation that doesn't take
+      // explicit embedding into account, we can build up the order on
+      // the fly, without following the level-based algorithm.
+      var order = [], m;
+      for (var i = 0; i < len;) {
+        if (countsAsLeft.test(types[i])) {
+          var start = i;
+          for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
+          order.push({from: start, to: i, level: 0});
+        } else {
+          var pos = i, at = order.length;
+          for (++i; i < len && types[i] != "L"; ++i) {}
+          for (var j = pos; j < i;) {
+            if (countsAsNum.test(types[j])) {
+              if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
+              var nstart = j;
+              for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
+              order.splice(at, 0, {from: nstart, to: j, level: 2});
+              pos = j;
+            } else ++j;
+          }
+          if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
+        }
+      }
+      if (order[0].level == 1 && (m = str.match(/^\s+/))) {
+        order[0].from = m[0].length;
+        order.unshift({from: 0, to: m[0].length, level: 0});
+      }
+      if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
+        lst(order).to -= m[0].length;
+        order.push({from: len - m[0].length, to: len, level: 0});
+      }
+      if (order[0].level != lst(order).level)
+        order.push({from: len, to: len, level: order[0].level});
+
+      return order;
+    };
+  })();
+
+  // THE END
+
+  CodeMirror.version = "3.12";
+
+  return CodeMirror;
+})();
diff --git a/Source/devtools/front_end/cm/css.js b/Source/devtools/front_end/cm/css.js
new file mode 100644
index 0000000..37ef76c
--- /dev/null
+++ b/Source/devtools/front_end/cm/css.js
@@ -0,0 +1,465 @@
+CodeMirror.defineMode("css", function(config) {
+  var indentUnit = config.indentUnit, type;
+  
+  var atMediaTypes = keySet([
+    "all", "aural", "braille", "handheld", "print", "projection", "screen",
+    "tty", "tv", "embossed"
+  ]);
+  
+  var atMediaFeatures = keySet([
+    "width", "min-width", "max-width", "height", "min-height", "max-height",
+    "device-width", "min-device-width", "max-device-width", "device-height",
+    "min-device-height", "max-device-height", "aspect-ratio",
+    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
+    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
+    "max-color", "color-index", "min-color-index", "max-color-index",
+    "monochrome", "min-monochrome", "max-monochrome", "resolution",
+    "min-resolution", "max-resolution", "scan", "grid"
+  ]);
+
+  var propertyKeywords = keySet([
+    "align-content", "align-items", "align-self", "alignment-adjust",
+    "alignment-baseline", "anchor-point", "animation", "animation-delay",
+    "animation-direction", "animation-duration", "animation-iteration-count",
+    "animation-name", "animation-play-state", "animation-timing-function",
+    "appearance", "azimuth", "backface-visibility", "background",
+    "background-attachment", "background-clip", "background-color",
+    "background-image", "background-origin", "background-position",
+    "background-repeat", "background-size", "baseline-shift", "binding",
+    "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
+    "bookmark-target", "border", "border-bottom", "border-bottom-color",
+    "border-bottom-left-radius", "border-bottom-right-radius",
+    "border-bottom-style", "border-bottom-width", "border-collapse",
+    "border-color", "border-image", "border-image-outset",
+    "border-image-repeat", "border-image-slice", "border-image-source",
+    "border-image-width", "border-left", "border-left-color",
+    "border-left-style", "border-left-width", "border-radius", "border-right",
+    "border-right-color", "border-right-style", "border-right-width",
+    "border-spacing", "border-style", "border-top", "border-top-color",
+    "border-top-left-radius", "border-top-right-radius", "border-top-style",
+    "border-top-width", "border-width", "bottom", "box-decoration-break",
+    "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
+    "caption-side", "clear", "clip", "color", "color-profile", "column-count",
+    "column-fill", "column-gap", "column-rule", "column-rule-color",
+    "column-rule-style", "column-rule-width", "column-span", "column-width",
+    "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
+    "cue-after", "cue-before", "cursor", "direction", "display",
+    "dominant-baseline", "drop-initial-after-adjust",
+    "drop-initial-after-align", "drop-initial-before-adjust",
+    "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
+    "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
+    "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
+    "float", "float-offset", "font", "font-feature-settings", "font-family",
+    "font-kerning", "font-language-override", "font-size", "font-size-adjust",
+    "font-stretch", "font-style", "font-synthesis", "font-variant",
+    "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
+    "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
+    "font-weight", "grid-cell", "grid-column", "grid-column-align",
+    "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
+    "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
+    "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
+    "icon", "image-orientation", "image-rendering", "image-resolution",
+    "inline-box-align", "justify-content", "left", "letter-spacing",
+    "line-break", "line-height", "line-stacking", "line-stacking-ruby",
+    "line-stacking-shift", "line-stacking-strategy", "list-style",
+    "list-style-image", "list-style-position", "list-style-type", "margin",
+    "margin-bottom", "margin-left", "margin-right", "margin-top",
+    "marker-offset", "marks", "marquee-direction", "marquee-loop",
+    "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
+    "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
+    "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
+    "outline-color", "outline-offset", "outline-style", "outline-width",
+    "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
+    "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
+    "page", "page-break-after", "page-break-before", "page-break-inside",
+    "page-policy", "pause", "pause-after", "pause-before", "perspective",
+    "perspective-origin", "pitch", "pitch-range", "play-during", "position",
+    "presentation-level", "punctuation-trim", "quotes", "rendering-intent",
+    "resize", "rest", "rest-after", "rest-before", "richness", "right",
+    "rotation", "rotation-point", "ruby-align", "ruby-overhang",
+    "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
+    "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
+    "tab-size", "table-layout", "target", "target-name", "target-new",
+    "target-position", "text-align", "text-align-last", "text-decoration",
+    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
+    "text-decoration-style", "text-emphasis", "text-emphasis-color",
+    "text-emphasis-position", "text-emphasis-style", "text-height",
+    "text-indent", "text-justify", "text-outline", "text-shadow",
+    "text-space-collapse", "text-transform", "text-underline-position",
+    "text-wrap", "top", "transform", "transform-origin", "transform-style",
+    "transition", "transition-delay", "transition-duration",
+    "transition-property", "transition-timing-function", "unicode-bidi",
+    "vertical-align", "visibility", "voice-balance", "voice-duration",
+    "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
+    "voice-volume", "volume", "white-space", "widows", "width", "word-break",
+    "word-spacing", "word-wrap", "z-index"
+  ]);
+
+  var colorKeywords = keySet([
+    "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
+    "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
+  ]);
+  
+  var valueKeywords = keySet([
+    "above", "absolute", "activeborder", "activecaption", "afar",
+    "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
+    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
+    "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
+    "backwards", "baseline", "below", "bidi-override", "binary", "bengali",
+    "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
+    "both", "bottom", "break-all", "break-word", "button", "button-bevel",
+    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
+    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
+    "cell", "center", "checkbox", "circle", "cjk-earthly-branch",
+    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
+    "col-resize", "collapse", "compact", "condensed", "contain", "content",
+    "content-box", "context-menu", "continuous", "copy", "cover", "crop",
+    "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
+    "decimal-leading-zero", "default", "default-button", "destination-atop",
+    "destination-in", "destination-out", "destination-over", "devanagari",
+    "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
+    "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
+    "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
+    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
+    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
+    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
+    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
+    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
+    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
+    "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
+    "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
+    "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
+    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
+    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
+    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
+    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
+    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
+    "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
+    "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
+    "landscape", "lao", "large", "larger", "left", "level", "lighter",
+    "line-through", "linear", "lines", "list-item", "listbox", "listitem",
+    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
+    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
+    "lower-roman", "lowercase", "ltr", "malayalam", "match",
+    "media-controls-background", "media-current-time-display",
+    "media-fullscreen-button", "media-mute-button", "media-play-button",
+    "media-return-to-realtime-button", "media-rewind-button",
+    "media-seek-back-button", "media-seek-forward-button", "media-slider",
+    "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
+    "media-volume-slider-container", "media-volume-sliderthumb", "medium",
+    "menu", "menulist", "menulist-button", "menulist-text",
+    "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
+    "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
+    "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
+    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
+    "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
+    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
+    "outside", "overlay", "overline", "padding", "padding-box", "painted",
+    "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
+    "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
+    "radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
+    "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
+    "ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
+    "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
+    "searchfield-cancel-button", "searchfield-decoration",
+    "searchfield-results-button", "searchfield-results-decoration",
+    "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
+    "single", "skip-white-space", "slide", "slider-horizontal",
+    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
+    "small", "small-caps", "small-caption", "smaller", "solid", "somali",
+    "source-atop", "source-in", "source-out", "source-over", "space", "square",
+    "square-button", "start", "static", "status-bar", "stretch", "stroke",
+    "sub", "subpixel-antialiased", "super", "sw-resize", "table",
+    "table-caption", "table-cell", "table-column", "table-column-group",
+    "table-footer-group", "table-header-group", "table-row", "table-row-group",
+    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
+    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
+    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
+    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
+    "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
+    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
+    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
+    "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
+    "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
+    "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
+    "xx-large", "xx-small", "yellow"
+  ]);
+
+  function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; }
+  function ret(style, tp) {type = tp; return style;}
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
+    else if (ch == "/" && stream.eat("*")) {
+      state.tokenize = tokenCComment;
+      return tokenCComment(stream, state);
+    }
+    else if (ch == "<" && stream.eat("!")) {
+      state.tokenize = tokenSGMLComment;
+      return tokenSGMLComment(stream, state);
+    }
+    else if (ch == "=") ret(null, "compare");
+    else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
+    else if (ch == "\"" || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    }
+    else if (ch == "#") {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("atom", "hash");
+    }
+    else if (ch == "!") {
+      stream.match(/^\s*\w*/);
+      return ret("keyword", "important");
+    }
+    else if (/\d/.test(ch)) {
+      stream.eatWhile(/[\w.%]/);
+      return ret("number", "unit");
+    }
+    else if (ch === "-") {
+      if (/\d/.test(stream.peek())) {
+        stream.eatWhile(/[\w.%]/);
+        return ret("number", "unit");
+      } else if (stream.match(/^[^-]+-/)) {
+        return ret("meta", "meta");
+      }
+    }
+    else if (/[,+>*\/]/.test(ch)) {
+      return ret(null, "select-op");
+    }
+    else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
+      return ret("qualifier", "qualifier");
+    }
+    else if (ch == ":") {
+      return ret("operator", ch);
+    }
+    else if (/[;{}\[\]\(\)]/.test(ch)) {
+      return ret(null, ch);
+    }
+    else if (ch == "u" && stream.match("rl(")) {
+      stream.backUp(1);
+      state.tokenize = tokenParenthesized;
+      return ret("property", "variable");
+    }
+    else {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("property", "variable");
+    }
+  }
+
+  function tokenCComment(stream, state) {
+    var maybeEnd = false, ch;
+    while ((ch = stream.next()) != null) {
+      if (maybeEnd && ch == "/") {
+        state.tokenize = tokenBase;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ret("comment", "comment");
+  }
+
+  function tokenSGMLComment(stream, state) {
+    var dashes = 0, ch;
+    while ((ch = stream.next()) != null) {
+      if (dashes >= 2 && ch == ">") {
+        state.tokenize = tokenBase;
+        break;
+      }
+      dashes = (ch == "-") ? dashes + 1 : 0;
+    }
+    return ret("comment", "comment");
+  }
+
+  function tokenString(quote, nonInclusive) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped)
+          break;
+        escaped = !escaped && ch == "\\";
+      }
+      if (!escaped) {
+        if (nonInclusive) stream.backUp(1);
+        state.tokenize = tokenBase;
+      }
+      return ret("string", "string");
+    };
+  }
+
+  function tokenParenthesized(stream, state) {
+    stream.next(); // Must be '('
+    if (!stream.match(/\s*[\"\']/, false))
+      state.tokenize = tokenString(")", true);
+    else
+      state.tokenize = tokenBase;
+    return ret(null, "(");
+  }
+
+  return {
+    startState: function(base) {
+      return {tokenize: tokenBase,
+              baseIndent: base || 0,
+              stack: []};
+    },
+
+    token: function(stream, state) {
+      
+      // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
+      // 
+      // rule** or **ruleset:
+      // A selector + braces combo, or an at-rule.
+      // 
+      // declaration block:
+      // A sequence of declarations.
+      // 
+      // declaration:
+      // A property + colon + value combo.
+      // 
+      // property value:
+      // The entire value of a property.
+      // 
+      // component value:
+      // A single piece of a property value. Like the 5px in
+      // text-shadow: 0 0 5px blue;. Can also refer to things that are
+      // multiple terms, like the 1-4 terms that make up the background-size
+      // portion of the background shorthand.
+      // 
+      // term:
+      // The basic unit of author-facing CSS, like a single number (5),
+      // dimension (5px), string ("foo"), or function. Officially defined
+      //  by the CSS 2.1 grammar (look for the 'term' production)
+      // 
+      // 
+      // simple selector:
+      // A single atomic selector, like a type selector, an attr selector, a
+      // class selector, etc.
+      // 
+      // compound selector:
+      // One or more simple selectors without a combinator. div.example is
+      // compound, div > .example is not.
+      // 
+      // complex selector:
+      // One or more compound selectors chained with combinators.
+      // 
+      // combinator:
+      // The parts of selectors that express relationships. There are four
+      // currently - the space (descendant combinator), the greater-than
+      // bracket (child combinator), the plus sign (next sibling combinator),
+      // and the tilda (following sibling combinator).
+      // 
+      // sequence of selectors:
+      // One or more of the named type of selector chained with commas.
+
+      if (state.tokenize == tokenBase && stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+
+      // Changing style returned based on context
+      var context = state.stack[state.stack.length-1];
+      if (style == "property") {
+        if (context == "propertyValue"){
+          if (valueKeywords[stream.current()]) {
+            style = "string-2";
+          } else if (colorKeywords[stream.current()]) {
+            style = "keyword";
+          } else {
+            style = "variable-2";
+          }
+        } else if (context == "rule") {
+          if (!propertyKeywords[stream.current()]) {
+            style += " error";
+          }
+        } else if (!context || context == "@media{") {
+          style = "tag";
+        } else if (context == "@media") {
+          if (atMediaTypes[stream.current()]) {
+            style = "attribute"; // Known attribute
+          } else if (/^(only|not)$/i.test(stream.current())) {
+            style = "keyword";
+          } else if (stream.current().toLowerCase() == "and") {
+            style = "error"; // "and" is only allowed in @mediaType
+          } else if (atMediaFeatures[stream.current()]) {
+            style = "error"; // Known property, should be in @mediaType(
+          } else {
+            // Unknown, expecting keyword or attribute, assuming attribute
+            style = "attribute error";
+          }
+        } else if (context == "@mediaType") {
+          if (atMediaTypes[stream.current()]) {
+            style = "attribute";
+          } else if (stream.current().toLowerCase() == "and") {
+            style = "operator";
+          } else if (/^(only|not)$/i.test(stream.current())) {
+            style = "error"; // Only allowed in @media
+          } else if (atMediaFeatures[stream.current()]) {
+            style = "error"; // Known property, should be in parentheses
+          } else {
+            // Unknown attribute or property, but expecting property (preceded
+            // by "and"). Should be in parentheses
+            style = "error";
+          }
+        } else if (context == "@mediaType(") {
+          if (propertyKeywords[stream.current()]) {
+            // do nothing, remains "property"
+          } else if (atMediaTypes[stream.current()]) {
+            style = "error"; // Known property, should be in parentheses
+          } else if (stream.current().toLowerCase() == "and") {
+            style = "operator";
+          } else if (/^(only|not)$/i.test(stream.current())) {
+            style = "error"; // Only allowed in @media
+          } else {
+            style += " error";
+          }
+        } else {
+          style = "error";
+        }
+      } else if (style == "atom") {
+        if(!context || context == "@media{") {
+          style = "builtin";
+        } else if (context == "propertyValue") {
+          if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
+            style += " error";
+          }
+        } else {
+          style = "error";
+        }
+      } else if (context == "@media" && type == "{") {
+        style = "error";
+      }
+
+      // Push/pop context stack
+      if (type == "{") {
+        if (context == "@media" || context == "@mediaType") {
+          state.stack.pop();
+          state.stack[state.stack.length-1] = "@media{";
+        }
+        else state.stack.push("rule");
+      }
+      else if (type == "}") {
+        state.stack.pop();
+        if (context == "propertyValue") state.stack.pop();
+      }
+      else if (type == "@media") state.stack.push("@media");
+      else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
+        state.stack.push("@mediaType");
+      else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
+      else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
+      else if (context == "@mediaType(" && type == ")") state.stack.pop();
+      else if (context == "rule" && type == ":") state.stack.push("propertyValue");
+      else if (context == "propertyValue" && type == ";") state.stack.pop();
+      return style;
+    },
+
+    indent: function(state, textAfter) {
+      var n = state.stack.length;
+      if (/^\}/.test(textAfter))
+        n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1;
+      return state.baseIndent + n * indentUnit;
+    },
+
+    electricChars: "}"
+  };
+});
+
+CodeMirror.defineMIME("text/css", "css");
diff --git a/Source/devtools/front_end/cm/htmlmixed.js b/Source/devtools/front_end/cm/htmlmixed.js
new file mode 100644
index 0000000..f2dd01d
--- /dev/null
+++ b/Source/devtools/front_end/cm/htmlmixed.js
@@ -0,0 +1,84 @@
+CodeMirror.defineMode("htmlmixed", function(config) {
+  var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
+  var jsMode = CodeMirror.getMode(config, "javascript");
+  var cssMode = CodeMirror.getMode(config, "css");
+
+  function html(stream, state) {
+    var style = htmlMode.token(stream, state.htmlState);
+    if (/(?:^|\s)tag(?:\s|$)/.test(style) && stream.current() == ">" && state.htmlState.context) {
+      if (/^script$/i.test(state.htmlState.context.tagName)) {
+        state.token = javascript;
+        state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
+      }
+      else if (/^style$/i.test(state.htmlState.context.tagName)) {
+        state.token = css;
+        state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
+      }
+    }
+    return style;
+  }
+  function maybeBackup(stream, pat, style) {
+    var cur = stream.current();
+    var close = cur.search(pat), m;
+    if (close > -1) stream.backUp(cur.length - close);
+    else if (m = cur.match(/<\/?$/)) {
+      stream.backUp(cur.length);
+      if (!stream.match(pat, false)) stream.match(cur[0]);
+    }
+    return style;
+  }
+  function javascript(stream, state) {
+    if (stream.match(/^<\/\s*script\s*>/i, false)) {
+      state.token = html;
+      state.localState = null;
+      return html(stream, state);
+    }
+    return maybeBackup(stream, /<\/\s*script\s*>/,
+                       jsMode.token(stream, state.localState));
+  }
+  function css(stream, state) {
+    if (stream.match(/^<\/\s*style\s*>/i, false)) {
+      state.token = html;
+      state.localState = null;
+      return html(stream, state);
+    }
+    return maybeBackup(stream, /<\/\s*style\s*>/,
+                       cssMode.token(stream, state.localState));
+  }
+
+  return {
+    startState: function() {
+      var state = htmlMode.startState();
+      return {token: html, localState: null, mode: "html", htmlState: state};
+    },
+
+    copyState: function(state) {
+      if (state.localState)
+        var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
+      return {token: state.token, localState: local, mode: state.mode,
+              htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
+    },
+
+    token: function(stream, state) {
+      return state.token(stream, state);
+    },
+
+    indent: function(state, textAfter) {
+      if (state.token == html || /^\s*<\//.test(textAfter))
+        return htmlMode.indent(state.htmlState, textAfter);
+      else if (state.token == javascript)
+        return jsMode.indent(state.localState, textAfter);
+      else
+        return cssMode.indent(state.localState, textAfter);
+    },
+
+    electricChars: "/{}:",
+
+    innerMode: function(state) {
+      var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode;
+      return {state: state.localState || state.htmlState, mode: mode};
+    }
+  };
+}, "xml", "javascript", "css");
+
+CodeMirror.defineMIME("text/html", "htmlmixed");
diff --git a/Source/devtools/front_end/cm/javascript.js b/Source/devtools/front_end/cm/javascript.js
new file mode 100644
index 0000000..b66d223
--- /dev/null
+++ b/Source/devtools/front_end/cm/javascript.js
@@ -0,0 +1,419 @@
+// TODO actually recognize syntax of TypeScript constructs
+
+CodeMirror.defineMode("javascript", function(config, parserConfig) {
+  var indentUnit = config.indentUnit;
+  var jsonMode = parserConfig.json;
+  var isTS = parserConfig.typescript;
+
+  // Tokenizer
+
+  var keywords = function(){
+    function kw(type) {return {type: type, style: "keyword"};}
+    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
+    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
+    
+    var jsKeywords = {
+      "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+      "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
+      "var": kw("var"), "const": kw("var"), "let": kw("var"),
+      "function": kw("function"), "catch": kw("catch"),
+      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+      "in": operator, "typeof": operator, "instanceof": operator,
+      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
+    };
+
+    // Extend the 'normal' keywords with the TypeScript language extensions
+    if (isTS) {
+      var type = {type: "variable", style: "variable-3"};
+      var tsKeywords = {
+        // object-like things
+        "interface": kw("interface"),
+        "class": kw("class"),
+        "extends": kw("extends"),
+        "constructor": kw("constructor"),
+
+        // scope modifiers
+        "public": kw("public"),
+        "private": kw("private"),
+        "protected": kw("protected"),
+        "static": kw("static"),
+
+        "super": kw("super"),
+
+        // types
+        "string": type, "number": type, "bool": type, "any": type
+      };
+
+      for (var attr in tsKeywords) {
+        jsKeywords[attr] = tsKeywords[attr];
+      }
+    }
+
+    return jsKeywords;
+  }();
+
+  var isOperatorChar = /[+\-*&%=<>!?|]/;
+
+  function chain(stream, state, f) {
+    state.tokenize = f;
+    return f(stream, state);
+  }
+
+  function nextUntilUnescaped(stream, end) {
+    var escaped = false, next;
+    while ((next = stream.next()) != null) {
+      if (next == end && !escaped)
+        return false;
+      escaped = !escaped && next == "\\";
+    }
+    return escaped;
+  }
+
+  // Used as scratch variables to communicate multiple values without
+  // consing up tons of objects.
+  var type, content;
+  function ret(tp, style, cont) {
+    type = tp; content = cont;
+    return style;
+  }
+
+  function jsTokenBase(stream, state) {
+    var ch = stream.next();
+    if (ch == '"' || ch == "'")
+      return chain(stream, state, jsTokenString(ch));
+    else if (/[\[\]{}\(\),;\:\.]/.test(ch))
+      return ret(ch);
+    else if (ch == "0" && stream.eat(/x/i)) {
+      stream.eatWhile(/[\da-f]/i);
+      return ret("number", "number");
+    }      
+    else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
+      stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
+      return ret("number", "number");
+    }
+    else if (ch == "/") {
+      if (stream.eat("*")) {
+        return chain(stream, state, jsTokenComment);
+      }
+      else if (stream.eat("/")) {
+        stream.skipToEnd();
+        return ret("comment", "comment");
+      }
+      else if (state.lastType == "operator" || state.lastType == "keyword c" ||
+               /^[\[{}\(,;:]$/.test(state.lastType)) {
+        nextUntilUnescaped(stream, "/");
+        stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
+        return ret("regexp", "string-2");
+      }
+      else {
+        stream.eatWhile(isOperatorChar);
+        return ret("operator", null, stream.current());
+      }
+    }
+    else if (ch == "#") {
+        stream.skipToEnd();
+        return ret("error", "error");
+    }
+    else if (isOperatorChar.test(ch)) {
+      stream.eatWhile(isOperatorChar);
+      return ret("operator", null, stream.current());
+    }
+    else {
+      stream.eatWhile(/[\w\$_]/);
+      var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
+      return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
+                     ret("variable", "variable", word);
+    }
+  }
+
+  function jsTokenString(quote) {
+    return function(stream, state) {
+      if (!nextUntilUnescaped(stream, quote))
+        state.tokenize = jsTokenBase;
+      return ret("string", "string");
+    };
+  }
+
+  function jsTokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize = jsTokenBase;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ret("comment", "comment");
+  }
+
+  // Parser
+
+  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
+
+  function JSLexical(indented, column, type, align, prev, info) {
+    this.indented = indented;
+    this.column = column;
+    this.type = type;
+    this.prev = prev;
+    this.info = info;
+    if (align != null) this.align = align;
+  }
+
+  function inScope(state, varname) {
+    for (var v = state.localVars; v; v = v.next)
+      if (v.name == varname) return true;
+  }
+
+  function parseJS(state, style, type, content, stream) {
+    var cc = state.cc;
+    // Communicate our context to the combinators.
+    // (Less wasteful than consing up a hundred closures on every call.)
+    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
+  
+    if (!state.lexical.hasOwnProperty("align"))
+      state.lexical.align = true;
+
+    while(true) {
+      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
+      if (combinator(type, content)) {
+        while(cc.length && cc[cc.length - 1].lex)
+          cc.pop()();
+        if (cx.marked) return cx.marked;
+        if (type == "variable" && inScope(state, content)) return "variable-2";
+        return style;
+      }
+    }
+  }
+
+  // Combinator utils
+
+  var cx = {state: null, column: null, marked: null, cc: null};
+  function pass() {
+    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
+  }
+  function cont() {
+    pass.apply(null, arguments);
+    return true;
+  }
+  function register(varname) {
+    function inList(list) {
+      for (var v = list; v; v = v.next)
+        if (v.name == varname) return true;
+      return false;
+    }
+    var state = cx.state;
+    if (state.context) {
+      cx.marked = "def";
+      if (inList(state.localVars)) return;
+      state.localVars = {name: varname, next: state.localVars};
+    } else {
+      if (inList(state.globalVars)) return;
+      state.globalVars = {name: varname, next: state.globalVars};
+    }
+  }
+
+  // Combinators
+
+  var defaultVars = {name: "this", next: {name: "arguments"}};
+  function pushcontext() {
+    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
+    cx.state.localVars = defaultVars;
+  }
+  function popcontext() {
+    cx.state.localVars = cx.state.context.vars;
+    cx.state.context = cx.state.context.prev;
+  }
+  function pushlex(type, info) {
+    var result = function() {
+      var state = cx.state;
+      state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
+    };
+    result.lex = true;
+    return result;
+  }
+  function poplex() {
+    var state = cx.state;
+    if (state.lexical.prev) {
+      if (state.lexical.type == ")")
+        state.indented = state.lexical.indented;
+      state.lexical = state.lexical.prev;
+    }
+  }
+  poplex.lex = true;
+
+  function expect(wanted) {
+    return function expecting(type) {
+      if (type == wanted) return cont();
+      else if (wanted == ";") return pass();
+      else return cont(arguments.callee);
+    };
+  }
+
+  function statement(type) {
+    if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
+    if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
+    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
+    if (type == "{") return cont(pushlex("}"), block, poplex);
+    if (type == ";") return cont();
+    if (type == "function") return cont(functiondef);
+    if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
+                                      poplex, statement, poplex);
+    if (type == "variable") return cont(pushlex("stat"), maybelabel);
+    if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
+                                         block, poplex, poplex);
+    if (type == "case") return cont(expression, expect(":"));
+    if (type == "default") return cont(expect(":"));
+    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+                                        statement, poplex, popcontext);
+    return pass(pushlex("stat"), expression, expect(";"), poplex);
+  }
+  function expression(type) {
+    if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
+    if (type == "function") return cont(functiondef);
+    if (type == "keyword c") return cont(maybeexpression);
+    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
+    if (type == "operator") return cont(expression);
+    if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
+    if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
+    return cont();
+  }
+  function maybeexpression(type) {
+    if (type.match(/[;\}\)\],]/)) return pass();
+    return pass(expression);
+  }
+    
+  function maybeoperator(type, value) {
+    if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
+    if (type == "operator" && value == "?") return cont(expression, expect(":"), expression);
+    if (type == ";") return;
+    if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
+    if (type == ".") return cont(property, maybeoperator);
+    if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
+  }
+  function maybelabel(type) {
+    if (type == ":") return cont(poplex, statement);
+    return pass(maybeoperator, expect(";"), poplex);
+  }
+  function property(type) {
+    if (type == "variable") {cx.marked = "property"; return cont();}
+  }
+  function objprop(type) {
+    if (type == "variable") cx.marked = "property";
+    if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
+  }
+  function commasep(what, end) {
+    function proceed(type) {
+      if (type == ",") return cont(what, proceed);
+      if (type == end) return cont();
+      return cont(expect(end));
+    }
+    return function commaSeparated(type) {
+      if (type == end) return cont();
+      else return pass(what, proceed);
+    };
+  }
+  function block(type) {
+    if (type == "}") return cont();
+    return pass(statement, block);
+  }
+  function maybetype(type) {
+    if (type == ":") return cont(typedef);
+    return pass();
+  }
+  function typedef(type) {
+    if (type == "variable"){cx.marked = "variable-3"; return cont();}
+    return pass();
+  }
+  function vardef1(type, value) {
+    if (type == "variable") {
+      register(value);
+      return isTS ? cont(maybetype, vardef2) : cont(vardef2);
+    }
+    return pass();
+  }
+  function vardef2(type, value) {
+    if (value == "=") return cont(expression, vardef2);
+    if (type == ",") return cont(vardef1);
+  }
+  function forspec1(type) {
+    if (type == "var") return cont(vardef1, expect(";"), forspec2);
+    if (type == ";") return cont(forspec2);
+    if (type == "variable") return cont(formaybein);
+    return cont(forspec2);
+  }
+  function formaybein(_type, value) {
+    if (value == "in") return cont(expression);
+    return cont(maybeoperator, forspec2);
+  }
+  function forspec2(type, value) {
+    if (type == ";") return cont(forspec3);
+    if (value == "in") return cont(expression);
+    return cont(expression, expect(";"), forspec3);
+  }
+  function forspec3(type) {
+    if (type != ")") cont(expression);
+  }
+  function functiondef(type, value) {
+    if (type == "variable") {register(value); return cont(functiondef);}
+    if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
+  }
+  function funarg(type, value) {
+    if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
+  }
+
+  // Interface
+
+  return {
+    startState: function(basecolumn) {
+      return {
+        tokenize: jsTokenBase,
+        lastType: null,
+        cc: [],
+        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
+        localVars: parserConfig.localVars,
+        globalVars: parserConfig.globalVars,
+        context: parserConfig.localVars && {vars: parserConfig.localVars},
+        indented: 0
+      };
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        if (!state.lexical.hasOwnProperty("align"))
+          state.lexical.align = false;
+        state.indented = stream.indentation();
+      }
+      if (stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+      if (type == "comment") return style;
+      state.lastType = type;
+      return parseJS(state, style, type, content, stream);
+    },
+
+    indent: function(state, textAfter) {
+      if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
+      if (state.tokenize != jsTokenBase) return 0;
+      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
+      if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
+      var type = lexical.type, closing = firstChar == type;
+      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
+      else if (type == "form" && firstChar == "{") return lexical.indented;
+      else if (type == "form") return lexical.indented + indentUnit;
+      else if (type == "stat")
+        return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
+      else if (lexical.info == "switch" && !closing)
+        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
+      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
+      else return lexical.indented + (closing ? 0 : indentUnit);
+    },
+
+    electricChars: ":{}",
+
+    jsonMode: jsonMode
+  };
+});
+
+CodeMirror.defineMIME("text/javascript", "javascript");
+CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
+CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
+CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
diff --git a/Source/devtools/front_end/cm/markselection.js b/Source/devtools/front_end/cm/markselection.js
new file mode 100644
index 0000000..d7ff30c
--- /dev/null
+++ b/Source/devtools/front_end/cm/markselection.js
@@ -0,0 +1,34 @@
+// Because sometimes you need to mark the selected *text*.
+//
+// Adds an option 'styleSelectedText' which, when enabled, gives
+// selected text the CSS class "CodeMirror-selectedtext".
+
+(function() {
+  "use strict";
+
+  CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
+    var prev = old && old != CodeMirror.Init;
+    if (val && !prev) {
+      updateSelectedText(cm);
+      cm.on("cursorActivity", updateSelectedText);
+    } else if (!val && prev) {
+      cm.off("cursorActivity", updateSelectedText);
+      clearSelectedText(cm);
+      delete cm._selectionMark;
+    }
+  });
+
+  function clearSelectedText(cm) {
+    if (cm._selectionMark) cm._selectionMark.clear();
+  }
+
+  function updateSelectedText(cm) {
+    clearSelectedText(cm);
+
+    if (cm.somethingSelected())
+      cm._selectionMark = cm.markText(cm.getCursor("start"), cm.getCursor("end"),
+                                      {className: "CodeMirror-selectedtext"});
+    else
+      cm._selectionMark = null;
+  }
+})();
diff --git a/Source/devtools/front_end/cm/matchbrackets.js b/Source/devtools/front_end/cm/matchbrackets.js
new file mode 100644
index 0000000..f4925b7
--- /dev/null
+++ b/Source/devtools/front_end/cm/matchbrackets.js
@@ -0,0 +1,74 @@
+(function() {
+  var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
+    (document.documentMode == null || document.documentMode < 8);
+
+  var Pos = CodeMirror.Pos;
+  // Disable brace matching in long lines, since it'll cause hugely slow updates  
+  var maxLineLen = 1000;
+
+  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
+  function findMatchingBracket(cm) {
+    var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
+    var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
+    if (!match) return null;
+    var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
+    var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
+
+    var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
+    function scan(line, lineNo, start) {
+      if (!line.text) return;
+      var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
+      if (start != null) pos = start + d;
+      for (; pos != end; pos += d) {
+        var ch = line.text.charAt(pos);
+        if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) {
+          var match = matching[ch];
+          if (match.charAt(1) == ">" == forward) stack.push(ch);
+          else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
+          else if (!stack.length) return {pos: pos, match: true};
+        }
+      }
+    }
+    for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
+      if (i == cur.line) found = scan(line, i, pos);
+      else found = scan(cm.getLineHandle(i), i);
+      if (found) break;
+    }
+    return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
+  }
+
+  function matchBrackets(cm, autoclear) {
+    var found = findMatchingBracket(cm);
+    if (!found || cm.getLine(found.from.line).length > maxLineLen ||
+       found.to && cm.getLine(found.to.line).length > maxLineLen)
+      return;
+
+    var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+    var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
+    var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
+    // Kludge to work around the IE bug from issue #1193, where text
+    // input stops going to the textare whever this fires.
+    if (ie_lt8 && cm.state.focused) cm.display.input.focus();
+    var clear = function() {
+      cm.operation(function() { one.clear(); two && two.clear(); });
+    };
+    if (autoclear) setTimeout(clear, 800);
+    else return clear;
+  }
+
+  var currentlyHighlighted = null;
+  function doMatchBrackets(cm) {
+    cm.operation(function() {
+      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+      if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
+    });
+  }
+
+  CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
+    if (val) cm.on("cursorActivity", doMatchBrackets);
+    else cm.off("cursorActivity", doMatchBrackets);
+  });
+
+  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+  CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
+})();
diff --git a/Source/devtools/front_end/cm/xml.js b/Source/devtools/front_end/cm/xml.js
new file mode 100644
index 0000000..7b11fd6
--- /dev/null
+++ b/Source/devtools/front_end/cm/xml.js
@@ -0,0 +1,324 @@
+CodeMirror.defineMode("xml", function(config, parserConfig) {
+  var indentUnit = config.indentUnit;
+  var Kludges = parserConfig.htmlMode ? {
+    autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
+                      'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
+                      'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
+                      'track': true, 'wbr': true},
+    implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
+                       'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
+                       'th': true, 'tr': true},
+    contextGrabbers: {
+      'dd': {'dd': true, 'dt': true},
+      'dt': {'dd': true, 'dt': true},
+      'li': {'li': true},
+      'option': {'option': true, 'optgroup': true},
+      'optgroup': {'optgroup': true},
+      'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
+            'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
+            'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
+            'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
+            'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
+      'rp': {'rp': true, 'rt': true},
+      'rt': {'rp': true, 'rt': true},
+      'tbody': {'tbody': true, 'tfoot': true},
+      'td': {'td': true, 'th': true},
+      'tfoot': {'tbody': true},
+      'th': {'td': true, 'th': true},
+      'thead': {'tbody': true, 'tfoot': true},
+      'tr': {'tr': true}
+    },
+    doNotIndent: {"pre": true},
+    allowUnquoted: true,
+    allowMissing: true
+  } : {
+    autoSelfClosers: {},
+    implicitlyClosed: {},
+    contextGrabbers: {},
+    doNotIndent: {},
+    allowUnquoted: false,
+    allowMissing: false
+  };
+  var alignCDATA = parserConfig.alignCDATA;
+
+  // Return variables for tokenizers
+  var tagName, type;
+
+  function inText(stream, state) {
+    function chain(parser) {
+      state.tokenize = parser;
+      return parser(stream, state);
+    }
+
+    var ch = stream.next();
+    if (ch == "<") {
+      if (stream.eat("!")) {
+        if (stream.eat("[")) {
+          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
+          else return null;
+        }
+        else if (stream.match("--")) return chain(inBlock("comment", "-->"));
+        else if (stream.match("DOCTYPE", true, true)) {
+          stream.eatWhile(/[\w\._\-]/);
+          return chain(doctype(1));
+        }
+        else return null;
+      }
+      else if (stream.eat("?")) {
+        stream.eatWhile(/[\w\._\-]/);
+        state.tokenize = inBlock("meta", "?>");
+        return "meta";
+      }
+      else {
+        var isClose = stream.eat("/");
+        tagName = "";
+        var c;
+        while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
+        if (!tagName) return "error";
+        type = isClose ? "closeTag" : "openTag";
+        state.tokenize = inTag;
+        return "tag";
+      }
+    }
+    else if (ch == "&") {
+      var ok;
+      if (stream.eat("#")) {
+        if (stream.eat("x")) {
+          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");          
+        } else {
+          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
+        }
+      } else {
+        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
+      }
+      return ok ? "atom" : "error";
+    }
+    else {
+      stream.eatWhile(/[^&<]/);
+      return null;
+    }
+  }
+
+  function inTag(stream, state) {
+    var ch = stream.next();
+    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
+      state.tokenize = inText;
+      type = ch == ">" ? "endTag" : "selfcloseTag";
+      return "tag";
+    }
+    else if (ch == "=") {
+      type = "equals";
+      return null;
+    }
+    else if (/[\'\"]/.test(ch)) {
+      state.tokenize = inAttribute(ch);
+      return state.tokenize(stream, state);
+    }
+    else {
+      stream.eatWhile(/[^\s\u00a0=<>\"\']/);
+      return "word";
+    }
+  }
+
+  function inAttribute(quote) {
+    return function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.next() == quote) {
+          state.tokenize = inTag;
+          break;
+        }
+      }
+      return "string";
+    };
+  }
+
+  function inBlock(style, terminator) {
+    return function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.match(terminator)) {
+          state.tokenize = inText;
+          break;
+        }
+        stream.next();
+      }
+      return style;
+    };
+  }
+  function doctype(depth) {
+    return function(stream, state) {
+      var ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == "<") {
+          state.tokenize = doctype(depth + 1);
+          return state.tokenize(stream, state);
+        } else if (ch == ">") {
+          if (depth == 1) {
+            state.tokenize = inText;
+            break;
+          } else {
+            state.tokenize = doctype(depth - 1);
+            return state.tokenize(stream, state);
+          }
+        }
+      }
+      return "meta";
+    };
+  }
+
+  var curState, setStyle;
+  function pass() {
+    for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
+  }
+  function cont() {
+    pass.apply(null, arguments);
+    return true;
+  }
+
+  function pushContext(tagName, startOfLine) {
+    var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
+    curState.context = {
+      prev: curState.context,
+      tagName: tagName,
+      indent: curState.indented,
+      startOfLine: startOfLine,
+      noIndent: noIndent
+    };
+  }
+  function popContext() {
+    if (curState.context) curState.context = curState.context.prev;
+  }
+
+  function element(type) {
+    if (type == "openTag") {
+      curState.tagName = tagName;
+      return cont(attributes, endtag(curState.startOfLine));
+    } else if (type == "closeTag") {
+      var err = false;
+      if (curState.context) {
+        if (curState.context.tagName != tagName) {
+          if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
+            popContext();
+          }
+          err = !curState.context || curState.context.tagName != tagName;
+        }
+      } else {
+        err = true;
+      }
+      if (err) setStyle = "error";
+      return cont(endclosetag(err));
+    }
+    return cont();
+  }
+  function endtag(startOfLine) {
+    return function(type) {
+      var tagName = curState.tagName;
+      curState.tagName = null;
+      if (type == "selfcloseTag" ||
+          (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
+        maybePopContext(tagName.toLowerCase());
+        return cont();
+      }
+      if (type == "endTag") {
+        maybePopContext(tagName.toLowerCase());
+        pushContext(tagName, startOfLine);
+        return cont();
+      }
+      return cont();
+    };
+  }
+  function endclosetag(err) {
+    return function(type) {
+      if (err) setStyle = "error";
+      if (type == "endTag") { popContext(); return cont(); }
+      setStyle = "error";
+      return cont(arguments.callee);
+    };
+  }
+  function maybePopContext(nextTagName) {
+    var parentTagName;
+    while (true) {
+      if (!curState.context) {
+        return;
+      }
+      parentTagName = curState.context.tagName.toLowerCase();
+      if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
+          !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
+        return;
+      }
+      popContext();
+    }
+  }
+
+  function attributes(type) {
+    if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
+    if (type == "endTag" || type == "selfcloseTag") return pass();
+    setStyle = "error";
+    return cont(attributes);
+  }
+  function attribute(type) {
+    if (type == "equals") return cont(attvalue, attributes);
+    if (!Kludges.allowMissing) setStyle = "error";
+    else if (type == "word") setStyle = "attribute";
+    return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
+  }
+  function attvalue(type) {
+    if (type == "string") return cont(attvaluemaybe);
+    if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
+    setStyle = "error";
+    return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
+  }
+  function attvaluemaybe(type) {
+    if (type == "string") return cont(attvaluemaybe);
+    else return pass();
+  }
+
+  return {
+    startState: function() {
+      return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        state.startOfLine = true;
+        state.indented = stream.indentation();
+      }
+      if (stream.eatSpace()) return null;
+
+      setStyle = type = tagName = null;
+      var style = state.tokenize(stream, state);
+      state.type = type;
+      if ((style || type) && style != "comment") {
+        curState = state;
+        while (true) {
+          var comb = state.cc.pop() || element;
+          if (comb(type || style)) break;
+        }
+      }
+      state.startOfLine = false;
+      return setStyle || style;
+    },
+
+    indent: function(state, textAfter, fullLine) {
+      var context = state.context;
+      if ((state.tokenize != inTag && state.tokenize != inText) ||
+          context && context.noIndent)
+        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
+      if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
+      if (context && /^<\//.test(textAfter))
+        context = context.prev;
+      while (context && !context.startOfLine)
+        context = context.prev;
+      if (context) return context.indent + indentUnit;
+      else return 0;
+    },
+
+    electricChars: "/",
+
+    configuration: parserConfig.htmlMode ? "html" : "xml"
+  };
+});
+
+CodeMirror.defineMIME("text/xml", "xml");
+CodeMirror.defineMIME("application/xml", "xml");
+if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
+  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
diff --git a/Source/devtools/front_end/cssNamedFlows.css b/Source/devtools/front_end/cssNamedFlows.css
new file mode 100644
index 0000000..f3387d5
--- /dev/null
+++ b/Source/devtools/front_end/cssNamedFlows.css
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 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 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.
+ */
+
+.css-named-flow-collections-view .split-view-sidebar {
+    overflow-x: hidden;
+}
+
+.css-named-flow-collections-view .tabbed-pane-header {
+    background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(236, 236, 236)), to(rgb(217, 217, 217)));
+}
+
+.css-named-flow-collections-view .info {
+    font-style: italic;
+    font-size: 10px;
+    margin-top: -5px;
+    position: absolute;
+    top: 50%;
+    text-align: center;
+    width: 100%;
+}
+
+.css-named-flow-collections-view .split-view-sidebar .sidebar-content {
+    bottom: 0px;
+    left: 0px;
+    padding: 0px;
+    position: absolute;
+    right: 0px;
+    top: 23px;
+}
+
+.css-named-flow-collections-view .split-view-sidebar .selection {
+    margin-left: -12px;
+    z-index: 0;
+}
+
+.css-named-flow-collections-view .split-view-contents .title {
+    position: relative;
+}
+
+.css-named-flow-collections-view .split-view-sidebar .named-flow-overflow::before, .css-named-flow-collections-view .region-empty:before, .css-named-flow-collections-view .region-fit::before, .css-named-flow-collections-view .region-overset::before {
+    cursor: default;
+    float: left;
+    height: 10px;
+    margin-top: 1px;
+    opacity: 0.75;
+    position: relative;
+    vertical-align: middle;
+    z-index: 1;
+}
+
+.css-named-flow-collections-view .split-view-sidebar .named-flow-overflow::before {
+    content: url(Images/namedFlowOverflow.png);
+    margin: 2px 3px 0px -13px;
+}
+
+.css-named-flow-collections-view .region-empty::before {
+    content: url(Images/regionEmpty.png);
+}
+
+.css-named-flow-collections-view .region-fit::before {
+    content: url(Images/regionFit.png);
+}
+
+.css-named-flow-collections-view .region-overset::before {
+    content: url(Images/regionOverset.png);
+}
+
+.css-named-flow-collections-view .split-view-contents .named-flow-element {
+    margin: 0px 0px 0px -24px;
+}
diff --git a/Source/devtools/front_end/dataGrid.css b/Source/devtools/front_end/dataGrid.css
new file mode 100644
index 0000000..ec8eac1
--- /dev/null
+++ b/Source/devtools/front_end/dataGrid.css
@@ -0,0 +1,253 @@
+.data-grid {
+    position: relative;
+    border: 1px solid #aaa;
+    font-size: 11px;
+    line-height: 120%;
+}
+
+.data-grid .highlight {
+    background-color: rgb(255, 230, 179);
+}
+
+.data-grid tr.selected .highlight {
+    background-color: transparent;
+}
+
+.data-grid table {
+    table-layout: fixed;
+    border-spacing: 0;
+    border-collapse: collapse;
+    width: 100%;
+}
+
+.data-grid .data-container {
+    position: absolute;
+    top: 16px;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    overflow-x: hidden;
+    overflow-y: overlay;
+}
+
+.data-grid.inline {
+    border-right: none;
+}
+
+.data-grid.inline .data-container {
+    position: static;
+}
+
+.data-grid.inline th.corner,
+.data-grid.inline td.corner {
+    display: none;
+}
+
+.data-grid th {
+    text-align: left;
+    background-image: url(Images/glossyHeader.png);
+    background-repeat: repeat-x;
+    border-right: 1px solid rgb(179, 179, 179);
+    border-bottom: 1px solid rgb(179, 179, 179);
+    height: 15px;
+    font-weight: normal;
+    vertical-align: middle;
+    padding: 0 4px;
+    white-space: nowrap;
+}
+
+.data-grid th.corner,
+.data-grid td.corner,
+.data-grid col.corner {
+    width: 14px;
+    padding-right: 0px;
+    padding-left: 0px;
+    border-right: 0 none transparent !important;
+}
+
+.data-grid tr.filler {
+    display: table-row !important;
+    height: auto !important;
+}
+
+.data-grid tr.filler td {
+    height: auto !important;
+    padding: 0 !important;
+}
+
+.data-grid table.data {
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    height: 100%;
+    border-top: 0 none transparent;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(white), color-stop(0.5, white), color-stop(0.5, rgb(234, 243, 255)), to(rgb(234, 243, 255)));
+    -webkit-background-size: 1px 32px;
+    table-layout: fixed;
+}
+
+.data-grid.inline table.data {
+    position: static;
+}
+
+.data-grid table.data tr {
+    display: none;
+}
+
+.data-grid table.data tr.revealed {
+    display: table-row;
+}
+
+.data-grid td {
+    vertical-align: top;
+    height: 16px; /* Keep in sync with .data-grid table.data @ -webkit-background-size */
+    line-height: 13px;
+    padding: 1px 4px;
+    white-space: nowrap;
+    overflow: hidden;
+    border-right: 1px solid #aaa;
+    -webkit-user-select: text;
+}
+
+.data-grid td > div, .data-grid th > div {
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.data-grid td.editing > div {
+    text-overflow: clip;
+}
+
+.data-grid .center div {
+    text-align: center;
+}
+
+.data-grid .right div {
+    text-align: right;
+}
+
+.data-grid th.sortable div {
+    position: relative;
+}
+
+.data-grid th.sortable:active {
+    background-image: url(Images/glossyHeaderPressed.png);
+}
+.data-grid th.sort-ascending, .data-grid th.sort-descending {
+    border-right: 1px solid rgb(107, 140, 196);
+    border-bottom: 1px solid rgb(107, 140, 196);
+    background-image: url(Images/glossyHeaderSelected.png);
+    background-repeat: repeat-x;
+}
+
+.data-grid th.sortable.sort-ascending:active, .data-grid th.sortable.sort-descending:active {
+    background-image: url(Images/glossyHeaderSelectedPressed.png);
+}
+
+.data-grid th.sort-ascending > div::after,
+.data-grid th.sort-descending > div::after {
+    position: absolute;
+    top: 1px;
+    right: 0;
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    opacity: 0.5;
+    width: 8px;
+    height: 10px;
+    content: "a";
+    color: transparent;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.data-grid th.sort-ascending > div::after,
+.data-grid th.sort-descending > div::after {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.data-grid th.sort-ascending > div::after {
+    background-position: -4px -108px;
+}
+
+.data-grid th.sort-descending > div::after {
+    background-position: -20px -96px;
+}
+
+.data-grid button {
+    line-height: 18px;
+    color: inherit;
+}
+
+body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-descending {
+    background-image: url(Images/glossyHeader.png);
+    border-right: 1px solid rgb(179, 179, 179);
+    border-bottom: 1px solid rgb(179, 179, 179);
+}
+
+.data-grid tr.parent td.disclosure::before {
+    -webkit-user-select: none;
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs.png);
+    -webkit-mask-size: 320px 120px;
+    float: left;
+    width: 8px;
+    margin-right: 2px;
+    content: "a";
+    color: transparent;
+    position: relative;
+    top: 1px;
+}
+
+.data-grid tr.parent td.disclosure::before {
+    background-color: rgb(110, 110, 110);
+    -webkit-mask-position: -4px -96px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.data-grid tr.parent td.disclosure::before {
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.data-grid tr.expanded td.disclosure::before {
+    -webkit-mask-position: -20px -96px;
+}
+
+.data-grid tr.selected {
+    background-color: rgb(212, 212, 212);
+    color: inherit;
+}
+
+.data-grid:focus tr.selected {
+    background-color: rgb(56, 121, 217);
+    color: white;
+}
+
+.data-grid:focus tr.selected a {
+    color: white;
+}
+
+.data-grid:focus tr.parent.selected td.disclosure::before {
+    background-color: white;
+    -webkit-mask-position: -4px -96px;
+}
+
+.data-grid:focus tr.expanded.selected td.disclosure::before {
+    background-color: white;
+    -webkit-mask-position: -20px -96px;
+}
+
+.data-grid tr:not(.parent) td.disclosure {
+    text-indent: 10px;
+}
+
+.data-grid-resizer {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    width: 5px;
+    z-index: 500;
+    cursor: col-resize;
+}
diff --git a/Source/devtools/front_end/dialog.css b/Source/devtools/front_end/dialog.css
new file mode 100644
index 0000000..64c9aac
--- /dev/null
+++ b/Source/devtools/front_end/dialog.css
@@ -0,0 +1,44 @@
+.dialog {
+    position: absolute;
+    
+    padding: 10px;
+    border-radius: 10px;
+    border: 1px solid gray;
+
+    -webkit-box-shadow: rgb(40,40,40) 0px 0px 50px;
+    
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+    
+    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#E9E9E9), to(#CFCFCF));
+    font-size: 11px;
+    font-family: 'Lucida Grande', sans-serif;
+}
+
+.dialog-contents {
+    width: 100%;
+    font-size: 11px;
+    font-family: 'Lucida Grande', sans-serif;
+}
+
+.go-to-line-dialog input {
+    font-size: 11px;
+}
+
+.go-to-line-dialog button {
+    font-size: 11px;
+    color: rgb(6, 6, 6);
+    border: 1px solid rgb(165, 165, 165);
+    background-color: rgb(237, 237, 237);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223)));
+    -webkit-border-radius: 12px;
+    -webkit-appearance: none;
+
+    padding: 3px 20px;
+    margin: 0 0 0 10px;
+}
+
+.go-to-line-dialog button:active {
+    background-color: rgb(215, 215, 215);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239)));
+}
diff --git a/Source/devtools/front_end/elementsPanel.css b/Source/devtools/front_end/elementsPanel.css
new file mode 100644
index 0000000..16f1161
--- /dev/null
+++ b/Source/devtools/front_end/elementsPanel.css
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+#elements-content {
+    padding-left: 0;
+}
+
+#elements-content > ol {
+    display: inline-block;
+    min-height: 100%;
+}
+
+#elements-content .editing {
+    margin-left: 8px;
+}
+
+#elements-content .elements-gutter-decoration {
+    position: absolute;
+    left: 1px;
+    margin-top: 2px;
+    height: 8px;
+    width: 8px;
+    border-radius: 4px;
+    border: 1px solid orange;
+    background-color: orange;
+}
+
+#elements-content .elements-gutter-decoration.elements-has-decorated-children {
+    opacity: 0.5;
+}
+
+#elements-content .CodeMirror {
+    /* Consistent with the .editing class in inspector.css */
+    -webkit-box-shadow: rgba(0, 0, 0, .5) 3px 3px 4px;
+    outline: 1px solid rgb(66%, 66%, 66%) !important;
+    background-color: white;
+}
+
+#elements-content .CodeMirror pre {
+    padding: 0;
+}
+
+#elements-content .CodeMirror-lines {
+    padding: 0;
+}
+
+.elements-tree-editor {
+    -webkit-user-select: text;
+    -webkit-user-modify: read-write-plaintext-only;
+}
+
+.metrics {
+    padding: 8px;
+    font-size: 10px;
+    text-align: center;
+    white-space: nowrap;
+}
+
+.metrics .label {
+    position: absolute;
+    font-size: 10px;
+    margin-left: 3px;
+    padding-left: 2px;
+    padding-right: 2px;
+}
+
+.metrics .position {
+    border: 1px rgb(66%, 66%, 66%) dotted;
+    background-color: white;
+    display: inline-block;
+    text-align: center;
+    padding: 3px;
+    margin: 3px;
+}
+
+.metrics .margin {
+    border: 1px dashed;
+    background-color: white;
+    display: inline-block;
+    text-align: center;
+    vertical-align: middle;
+    padding: 3px;
+    margin: 3px;
+}
+
+.metrics .border {
+    border: 1px black solid;
+    background-color: white;
+    display: inline-block;
+    text-align: center;
+    vertical-align: middle;
+    padding: 3px;
+    margin: 3px;
+}
+
+.metrics .padding {
+    border: 1px grey dashed;
+    background-color: white;
+    display: inline-block;
+    text-align: center;
+    vertical-align: middle;
+    padding: 3px;
+    margin: 3px;
+}
+
+.metrics .content {
+    position: static;
+    border: 1px gray solid;
+    background-color: white;
+    display: inline-block;
+    text-align: center;
+    vertical-align: middle;
+    padding: 3px;
+    margin: 3px;
+    min-width: 80px;
+    text-align: center;
+    overflow: visible;
+}
+
+.metrics .content span {
+    display: inline-block;
+}
+
+.metrics .editing {
+    position: relative;
+    z-index: 100;
+}
+
+.metrics .left {
+    display: inline-block;
+    vertical-align: middle;
+}
+
+.metrics .right {
+    display: inline-block;
+    vertical-align: middle;
+}
+
+.metrics .top {
+    display: inline-block;
+}
+
+.metrics .bottom {
+    display: inline-block;
+}
+
+
+.styles-section {
+    padding: 2px 2px 4px 4px;
+    min-height: 18px;
+    white-space: nowrap;
+    -webkit-background-origin: padding;
+    -webkit-background-clip: padding;
+    -webkit-user-select: text;
+}
+
+.styles-section:not(.first-styles-section) {
+    border-top: 1px solid rgb(191, 191, 191);
+}
+
+.styles-section.read-only {
+    background-color: rgb(240, 240, 240);
+}
+
+.styles-section .properties li.not-parsed-ok {
+    margin-left: 0px;
+}
+
+.styles-section.computed-style .properties li.not-parsed-ok {
+    margin-left: -6px;
+}
+
+.styles-section .properties li.not-parsed-ok .exclamation-mark {
+    position: relative;
+    width: 12px;
+    margin: 0 6px 0 0;
+    top: 1px;
+    left: -36px; /* outdent to compensate for the top-level property indent */
+    -webkit-user-select: none;
+    cursor: default;
+    z-index: 1;
+}
+
+.styles-section .header {
+    white-space: nowrap;
+    -webkit-background-origin: padding;
+    -webkit-background-clip: padding;
+}
+
+.styles-section .header .title {
+    word-wrap: break-word;
+    white-space: normal;
+}
+
+.styles-section .header .title .media, .styles-section .header .title .media .subtitle {
+    color: rgb(128, 128, 128);
+    overflow: hidden;
+}
+
+.styles-section .header .subtitle {
+    color: rgb(85, 85, 85);
+    float: right;
+    margin-left: 5px;
+    max-width: 100%;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+}
+
+.styles-section .header .subtitle a {
+    color: inherit;
+}
+
+.styles-section .selector {
+    color: #888;
+}
+
+.styles-section .selector-matches {
+    color: #222;
+}
+
+.styles-section a[data-uncopyable] {
+    display: inline-block;
+}
+
+.styles-section a[data-uncopyable]::before {
+    content: attr(data-uncopyable);
+    text-decoration: underline;
+}
+
+.styles-section .properties {
+    display: none;
+    margin: 0;
+    padding: 2px 4px 0 0;
+    list-style: none;
+    clear: both;
+}
+
+.styles-section.matched-styles .properties {
+    padding-left: 0;
+}
+
+.styles-section.no-affect .properties li {
+    opacity: 0.5;
+}
+
+.styles-section.no-affect .properties li.editing {
+    opacity: 1.0;
+}
+
+.styles-section.expanded .properties {
+    display: block;
+}
+
+.styles-section .properties li {
+    margin-left: 12px;
+    padding-left: 22px;
+    padding-top: 0;
+    white-space: normal;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    cursor: auto;
+}
+
+.styles-section.computed-style.expanded .properties > li {
+    padding-left: 0;
+}
+
+.styles-section.computed-style.expanded .properties > li .webkit-css-property {
+    margin-left: 0;
+}
+
+.styles-section .properties li .webkit-css-property {
+    margin-left: -22px; /* outdent the first line of longhand properties (in an expanded shorthand) to compensate for the "padding-left" shift in .styles-section .properties li */
+}
+
+.styles-section.expanded .properties > li {
+    padding-left: 38px;
+}
+
+.styles-section .properties > li .webkit-css-property {
+    margin-left: -38px; /* outdent the first line of the top-level properties to compensate for the "padding-left" shift in .styles-section .properties > li */
+}
+
+.styles-section .properties > li.child-editing {
+    padding-left: 8px;
+}
+
+.styles-section .properties > li.child-editing .webkit-css-property {
+    margin-left: 0;
+}
+
+.styles-section.matched-styles .properties li {
+    margin-left: 0 !important;
+}
+
+.styles-section .properties li.child-editing {
+    word-wrap: break-word !important;
+    white-space: normal !important;
+    padding-left: 0;
+}
+
+.styles-section .properties ol {
+    display: none;
+    margin: 0;
+    -webkit-padding-start: 12px;
+    list-style: none;
+}
+
+.styles-section .properties ol.expanded {
+    display: block;
+}
+
+.styles-section.matched-styles .properties li.parent .expand-element {
+    -webkit-user-select: none;
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    margin-right: 2px;
+    margin-left: -6px;
+    opacity: 0.55;
+    width: 8px;
+    height: 10px;
+    display: inline-block;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.styles-section.matched-styles .properties li.parent .expand-element {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.styles-section.matched-styles .properties li.parent .expand-element {
+    background-position: -4px -96px;
+}
+
+.styles-section.matched-styles .properties li.parent.expanded .expand-element {
+    background-position: -20px -96px;
+}
+
+.styles-section .properties li .info {
+    padding-top: 4px;
+    padding-bottom: 3px;
+}
+
+.styles-section.matched-styles:not(.read-only):hover .properties .enabled-button {
+    visibility: visible;
+}
+
+.styles-section.matched-styles:not(.read-only) .properties li.disabled .enabled-button {
+    visibility: visible;
+}
+
+.styles-section .properties .enabled-button {
+    visibility: hidden;
+    float: left;
+    font-size: 10px;
+    margin: 0;
+    vertical-align: top;
+    position: relative;
+    z-index: 1;
+    width: 18px;
+    left: -40px; /* original -2px + (-38px) to compensate for the first line outdent */
+    top: 1px;
+}
+
+.styles-section.matched-styles .properties ol.expanded {
+    margin-left: 16px;
+}
+
+.styles-section .properties .overloaded,
+.styles-section .properties .inactive,
+.styles-section .properties .disabled,
+.styles-section .properties .not-parsed-ok {
+    text-decoration: line-through;
+}
+
+.styles-section.computed-style .properties .disabled {
+    text-decoration: none;
+    opacity: 0.5;
+}
+
+.styles-section .properties .implicit, .styles-section .properties .inherited {
+    opacity: 0.5;
+}
+
+.styles-element-state-pane {
+    background-color: rgb(240, 240, 240);
+    overflow: hidden;
+    margin-top: -38px;
+    -webkit-transition: margin-top 0.1s ease-in-out;
+    padding-left: 2px;
+}
+
+.styles-element-state-pane.expanded {
+    border-bottom: 1px solid rgb(189, 189, 189);
+    margin-top: 0;
+}
+
+.styles-element-state-pane > table {
+    width: 100%;
+    border-spacing: 0;
+}
+
+.styles-element-state-pane input {
+    margin: 2px;
+    vertical-align: -2px;
+}
+
+.styles-selector {
+    cursor: text;
+}
+
+.body .styles-section .properties .inherited {
+    display: none;
+}
+
+.body.show-inherited .styles-section .properties .inherited {
+    display: block;
+}
+
+.add-attribute {
+    margin-left: 1px;
+    margin-right: 1px;
+    white-space: nowrap;
+}
+
+.section .event-bars {
+    display: none;
+}
+
+.section.expanded .event-bars {
+    display: block;
+}
+
+.event-bar {
+    position: relative;
+    margin-left: 10px;
+}
+
+.event-bar:first-child {
+    margin-top: 1px;
+}
+
+.event-bars .event-bar .header {
+    padding: 0 8px 0 6px;
+    min-height: 16px;
+    opacity: 1.0;
+    white-space: nowrap;
+    -webkit-background-origin: padding;
+    -webkit-background-clip: padding;
+}
+
+.event-bars .event-bar .header .title {
+    font-weight: normal;
+    text-shadow: white 0 1px 0;
+}
+
+.event-bars .event-bar .header .subtitle {
+    color: rgba(90, 90, 90, 0.75);
+}
+
+.event-bars .event-bar .header::before {
+    -webkit-user-select: none;
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    opacity: 0.5;
+    content: "a";
+    color: transparent;
+    text-shadow: none;
+    float: left;
+    width: 8px;
+    margin-right: 4px;
+    margin-top: 2px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.event-bars .event-bar .header::before {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.event-bars .event-bar .header::before {
+    background-position: -4px -96px;
+}
+
+.event-bars .event-bar.expanded .header::before {
+    background-position: -20px -96px;
+}
+
+.image-preview-container {
+    background: transparent;
+    text-align: center;
+}
+
+.image-preview-container img {
+    margin: 2px auto;
+    max-width: 100px;
+    max-height: 100px;
+    background-image: url(Images/checker.png);
+    -webkit-user-select: text;
+    -webkit-user-drag: auto;
+}
+
+
+.sidebar-pane.composite {
+    overflow: hidden;
+    position: absolute;
+}
+
+.sidebar-pane.composite > .body {
+    height: 100%;
+}
+
+.sidebar-pane.composite .metrics {
+    border-bottom: 1px solid rgb(64%, 64%, 64%);
+    height: 206px;
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+    -webkit-align-items: center;
+    -webkit-justify-content: center;
+}
+
+.sidebar-pane.composite .metrics-and-computed .sidebar-pane-toolbar {
+    margin-top: 4px;
+    margin-bottom: -4px;
+    position: relative;
+}
+
+.sidebar-pane.composite .sidebar-pane-toolbar > .sidebar-pane-subtitle {
+    left: 8px;
+}
+
+.sidebar-pane.composite .styles-section.read-only {
+    background-color: inherit;
+}
+
+.panel.elements .sidebar-pane-toolbar > select {
+    float: right;
+    width: 23px;
+    height: 17px;
+    color: transparent;
+    background-color: transparent;
+    border: none;
+    background-repeat: no-repeat;
+    margin: 1px 0 0 0;
+    padding: 0;
+    -webkit-border-radius: 0;
+    -webkit-appearance: none;
+}
+
+.panel.elements .sidebar-pane-toolbar > select:hover {
+    background-position: -23px 0px;
+}
+
+.panel.elements .sidebar-pane-toolbar > select:active {
+    background-position: -46px 0px;
+}
+
+.panel.elements .sidebar-pane-toolbar > select.select-settings {
+    background-image: url(Images/paneSettingsButtons.png);
+}
+
+.panel.elements .sidebar-pane-toolbar > select.select-filter {
+    background-image: url(Images/paneFilterButtons.png);
+}
+.panel.elements .sidebar-pane-toolbar > select > option, .panel.elements .sidebar-pane-toolbar > select > hr {
+    color: black;
+}
diff --git a/Source/devtools/front_end/externs.js b/Source/devtools/front_end/externs.js
new file mode 100644
index 0000000..a3e399a
--- /dev/null
+++ b/Source/devtools/front_end/externs.js
@@ -0,0 +1,463 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+// WebKit Web Facing API
+var console = {}
+/** @param {...*} vararg */
+console.warn = function(vararg) {}
+/** @param {...*} vararg */
+console.assert = function(vararg) {}
+/** @param {...*} vararg */
+console.error = function(vararg) {}
+console.trace = function() {}
+
+/** @param {boolean=} param */
+Element.prototype.scrollIntoViewIfNeeded = function(param) {}
+/** @type {boolean} */
+Event.prototype.isMetaOrCtrlForTest = false;
+/** @param {...*} vararg */
+Event.prototype.initWebKitWheelEvent = function(vararg) {}
+Event.prototype.stopImmediatePropagation = function() {}
+
+/** @param {Element} element */
+window.getComputedStyle = function(element) {}
+/** @param {*} message */
+function postMessage(message) {}
+
+/** @type {*} */
+window.testRunner = null;
+
+/**
+ * @constructor
+ */
+function WebKitMutation(callback)
+{
+    this.type = "";
+    /** @type {Node} */ this.target = null;
+    /** @type {Array.<Node>} */ this.addedNodes = [];
+    /** @type {Array.<Node>} */ this.removedNodes = [];
+}
+
+/**
+ * @constructor
+ * @param {function(Array.<WebKitMutation>)} callback
+ */
+function WebKitMutationObserver(callback) {}
+/** 
+ * @param {Node} container
+ * @param {Object} options
+ */
+WebKitMutationObserver.prototype.observe = function(container, options) {}
+WebKitMutationObserver.prototype.disconnect = function() {}
+
+/**
+ * @param {string} eventName
+ * @param {Function} listener
+ * @param {boolean=} capturing
+ */
+function addEventListener(eventName, listener, capturing) {}
+
+/** @param {boolean=} onlyFirst */
+Array.prototype.remove = function(obj, onlyFirst) {}
+Array.prototype.keySet = function() {}
+/** @return {number} */
+Array.prototype.upperBound = function(anchor) {}
+/** @return {number} */
+Array.prototype.binaryIndexOf = function(anchor) {}
+Array.prototype.sortRange = function(comparator, leftBound, rightBound, k) {}
+
+/**
+ * @this {Array.<number>}
+ * @param {function(number,number):boolean} comparator
+ * @param {number} left
+ * @param {number} right
+ * @param {number} pivotIndex
+ * @return {number}
+ */
+Array.prototype.partition = function(comparator, left, right, pivotIndex) {}
+
+/**
+ * @this {Array.<number>}
+ * @param {number} k
+ * @param {function(number,number):boolean=} comparator
+ * @return {number}
+ */
+Array.prototype.qselect = function(k, comparator) {}
+
+/**
+ * @this {Array.<*>}
+ * @param {string} field
+ * @return {Array.<*>}
+ */
+Array.prototype.select = function(field) {}
+
+/**
+ * @this {Array.<*>}
+ * @return {*}
+ */
+Array.prototype.peekLast = function() {}
+
+DOMApplicationCache.prototype.UNCACHED = 0;
+DOMApplicationCache.prototype.IDLE = 1;
+DOMApplicationCache.prototype.CHECKING = 2;
+DOMApplicationCache.prototype.DOWNLOADING = 3;
+DOMApplicationCache.prototype.UPDATEREADY = 4;
+DOMApplicationCache.prototype.OBSOLETE = 5;
+
+// File System API
+/**
+ * @constructor
+ */
+function DOMFileSystem() {}
+
+/**
+ * @type {DirectoryEntry}
+ */
+DOMFileSystem.prototype.root = null;
+
+/** @type {Node} */
+Range.prototype.startContainer;
+
+// Inspector Backend
+var InspectorBackend = {}
+InspectorBackend.runAfterPendingDispatches = function(message) {}
+
+/** @interface */
+function InspectorFrontendHostAPI() {}
+InspectorFrontendHostAPI.prototype.platform = function() {}
+InspectorFrontendHostAPI.prototype.port = function() {}
+InspectorFrontendHostAPI.prototype.bringToFront = function() {}
+InspectorFrontendHostAPI.prototype.closeWindow = function() {}
+InspectorFrontendHostAPI.prototype.requestSetDockSide = function(dockSide) {}
+InspectorFrontendHostAPI.prototype.setAttachedWindowHeight = function(height) {}
+InspectorFrontendHostAPI.prototype.moveWindowBy = function(x, y) {}
+InspectorFrontendHostAPI.prototype.setInjectedScriptForOrigin = function(origin, script) {}
+InspectorFrontendHostAPI.prototype.loaded = function() {}
+InspectorFrontendHostAPI.prototype.localizedStringsURL = function() {}
+InspectorFrontendHostAPI.prototype.inspectedURLChanged = function(url) {}
+InspectorFrontendHostAPI.prototype.documentCopy = function(event) {}
+InspectorFrontendHostAPI.prototype.copyText = function(text) {}
+InspectorFrontendHostAPI.prototype.openInNewTab = function(url) {}
+InspectorFrontendHostAPI.prototype.canSave = function() {}
+InspectorFrontendHostAPI.prototype.save = function(url, content, forceSaveAs) {}
+InspectorFrontendHostAPI.prototype.close = function(url) {}
+InspectorFrontendHostAPI.prototype.append = function(url, content) {}
+InspectorFrontendHostAPI.prototype.sendMessageToBackend = function(message) {}
+InspectorFrontendHostAPI.prototype.recordActionTaken = function(actionCode) {}
+InspectorFrontendHostAPI.prototype.recordPanelShown = function(panelCode) {}
+InspectorFrontendHostAPI.prototype.recordSettingChanged = function(settingCode) {}
+InspectorFrontendHostAPI.prototype.loadResourceSynchronously = function(url) {}
+InspectorFrontendHostAPI.prototype.supportsFileSystems = function() {}
+InspectorFrontendHostAPI.prototype.requestFileSystems = function() {}
+InspectorFrontendHostAPI.prototype.addFileSystem = function() {}
+InspectorFrontendHostAPI.prototype.removeFileSystem = function(fileSystemPath) {}
+InspectorFrontendHostAPI.prototype.isolatedFileSystem = function(fileSystemId, registeredName) {}
+InspectorFrontendHostAPI.prototype.setZoomFactor = function(zoom) {}
+/** @type {InspectorFrontendHostAPI} */
+var InspectorFrontendHost;
+
+/** @constructor */
+function SourceMapV3()
+{
+    /** @type {number} */ this.version;
+    /** @type {string} */ this.file;
+    /** @type {Array.<string>} */ this.sources;
+    /** @type {Array.<SourceMapV3.Section>} */ this.sections;
+    /** @type {string} */ this.mappings;
+    /** @type {string} */ this.sourceRoot;
+}
+
+/** @constructor */
+SourceMapV3.Section = function()
+{
+    /** @type {SourceMapV3} */ this.map;
+    /** @type {SourceMapV3.Offset} */ this.offset;
+}
+
+/** @constructor */
+SourceMapV3.Offset = function()
+{
+    /** @type {number} */ this.line;
+    /** @type {number} */ this.column;
+}
+
+// FIXME: remove everything below.
+var WebInspector = {}
+
+WebInspector.queryParamsObject = {}
+WebInspector.toggleSearchingForNode = function() {}
+WebInspector.panels = {};
+
+/**
+ * @param {Element} element
+ * @param {function()=} onclose
+ */
+WebInspector.showViewInDrawer = function(element, view, onclose) {}
+
+WebInspector.closeViewInDrawer = function() {}
+
+/**
+ * @param {string=} messageLevel
+ * @param {boolean=} showConsole
+ */
+WebInspector.log = function(message, messageLevel, showConsole) {}
+
+WebInspector.showErrorMessage = function(error) {}
+
+WebInspector.addMainEventListeners = function(doc) {}
+
+WebInspector.openResource = function(url, external) {}
+
+WebInspector.showConsole = function() {}
+
+/**
+ * @param {string} expression
+ * @param {boolean=} showResultOnly
+ */
+WebInspector.evaluateInConsole = function(expression, showResultOnly) {}
+
+WebInspector.queryParamsObject = {}
+
+WebInspector.Events = {
+    InspectorLoaded: "InspectorLoaded",
+    InspectorClosing: "InspectorClosing"
+}
+
+/** Extensions API */
+
+/** @constructor */
+function AuditCategory() {}
+/** @constructor */
+function AuditResult() {}
+/** @constructor */
+function EventSink() {}
+/** @constructor */
+function ExtensionSidebarPane() {}
+/** @constructor */
+function Panel() {}
+/** @constructor */
+function PanelWithSidebar() {}
+/** @constructor */
+function Request() {}
+/** @constructor */
+function Resource() {}
+/** @constructor */
+function Timeline() {}
+
+var extensionServer;
+
+/** @type {string} */
+Location.prototype.origin = "";
+
+/**
+ * @constructor
+ */
+function ExtensionDescriptor() {
+    this.startPage = "";
+    this.name = "";
+}
+
+/**
+ * @constructor
+ */
+function ExtensionReloadOptions() {
+    this.ignoreCache = false;
+    this.injectedScript = "";
+    this.userAgent = "";
+}
+
+WebInspector.showPanel = function(panel)
+{
+}
+
+/**
+ * @param {ExtensionDescriptor} extensionInfo
+ * @return {string}
+ */
+function buildPlatformExtensionAPI(extensionInfo) {}
+
+/**
+ * @type {string} 
+ */
+WebInspector.inspectedPageDomain;
+
+WebInspector.SourceJavaScriptTokenizer = {}
+WebInspector.SourceJavaScriptTokenizer.Keywords = {}
+
+var InspectorTest = {}
+
+/* jsdifflib API */
+var difflib = {};
+difflib.stringAsLines = function(text) { return []; }
+/** @constructor */
+difflib.SequenceMatcher = function(baseText, newText) { }
+difflib.SequenceMatcher.prototype.get_opcodes = function() { return []; }
+
+/** @constructor */
+WebInspector.AceTextEditor = function(url, delegate) { }
+
+/** @constructor */
+var CodeMirror = function() { }
+CodeMirror.prototype = {
+    addKeyMap: function(map) { },
+    addLineClass: function(handle, where, cls) { },
+    addLineWidget: function(handle, node, options) { },
+    /**
+     * @param {string|Object} spec
+     * @param {Object=} options
+     */
+    addOverlay: function(spec, options) { },
+    addWidget: function(pos, node, scroll, vert, horiz) { },
+    charCoords: function(pos, mode) { },
+    clearGutter: function(gutterID) { },
+    clearHistory: function() { },
+    clipPos: function(pos) { },
+    coordsChar: function(coords, mode) { },
+    cursorCoords: function(start, mode) { },
+    defaultCharWidth: function() { },
+    defaultTextHeight: function() { },
+    deleteH: function(dir, unit) { },
+    eachLine: function(from, to, op) { },
+    execCommand: function(cmd) { },
+    extendSelection: function(from, to) { },
+    findMarksAt: function(pos) { },
+    findMatchingBracket: function() { },
+    findPosH: function(from, amount, unit, visually) { },
+    findPosV: function(from, amount, unit, goalColumn) { },
+    firstLine: function() { },
+    focus: function() { },
+    getAllMarks: function() { },
+    getCursor: function(start) { },
+    getDoc: function() { },
+    getGutterElement: function() { },
+    getHistory: function() { },
+    getInputField: function(){ },
+    getLine: function(line) { },
+    /**
+     * @return {{wrapClass: string}}
+     */
+    getLineHandle: function(line) { },
+    getLineNumber: function(line) { },
+    getMode: function() { },
+    getOption: function(option) { },
+    getRange: function(from, to, lineSep) { },
+    getScrollInfo: function() { },
+    getScrollerElement: function() { },
+    getSelection: function() { },
+    getStateAfter: function(line) { },
+    getTokenAt: function(pos) { },
+    getValue: function(lineSep) { },
+    getViewport: function() { },
+    getWrapperElement: function() { },
+    hasFocus: function() { },
+    historySize: function() { },
+    indentLine: function(n, dir, aggressive) { },
+    indentSelection: function(how) { },
+    indexFromPos: function(coords) { },
+    isClean: function() { },
+    iterLinkedDocs: function(f) { },
+    lastLine: function() { },
+    lineCount: function() { },
+    lineInfo: function(line) { },
+    linkedDoc: function(options) { },
+    markClean: function() { },
+    markText: function(from, to, options) { },
+    moveH: function(dir, unit) { },
+    moveV: function(dir, unit) { },
+    off: function(type, f) { },
+    on: function(type, f) { },
+    operation: function(f) { },
+    posFromIndex: function(off) { },
+    redo: function() { },
+    refresh: function() { },
+    removeKeyMap: function(map) { },
+    removeLine: function(line) { },
+    removeLineClass: function(handle, where, cls) { },
+    removeLineWidget: function(widget) { },
+    removeOverlay: function(spec) { },
+    replaceRange: function(code, from, to, origin) { },
+    replaceSelection: function(code, collapse, origin) { },
+    scrollIntoView: function(pos, margin) { },
+    scrollTo: function(x, y) { },
+    setBookmark: function(pos, options) { },
+    setCursor: function(line, ch, extend) { },
+    setExtending: function(val) { },
+    setGutterMarker: function(line, gutterID, value) { },
+    setHistory: function(histData) { },
+    setLine: function(line, text) { },
+    setOption: function(option, value) { },
+    setSelection: function(anchor, head) { },
+    setSize: function(width, height) { },
+    setValue: function(code) { },
+    somethingSelected: function() { },
+    swapDoc: function(doc) { },
+    undo: function() { },
+    unlinkDoc: function(other) { }
+}
+/** @type {number} */
+CodeMirror.prototype.lineCount;
+CodeMirror.Pass;
+
+/** @constructor */
+CodeMirror.Pos = function(line, ch) { }
+/** type {number} */
+CodeMirror.Pos.prototype.line;
+/** type {number} */
+CodeMirror.Pos.prototype.ch;
+
+/** @constructor */
+CodeMirror.StringStream = function() { }
+CodeMirror.StringStream.prototype = {
+    backUp: function (n) { },
+    column: function () { },
+    current: function () { },
+    eat: function (match) { },
+    eatSpace: function () { },
+    eatWhile: function (match) { },
+    eol: function () { },
+    indentation: function () { },
+    /**
+     * @param {RegExp|string} pattern
+     * @param {boolean=} consume
+     * @param {boolean=} caseInsensitive
+     */
+    match: function (pattern, consume, caseInsensitive) { },
+    next: function () { },
+    peek: function () { },
+    skipTo: function (ch) { },
+    skipToEnd: function () { },
+    sol: function () { }
+}
+
+WebInspector.suggestReload = function() { }
+WebInspector.reload = function() { }
+
+/** @type {boolean} */
+window.dispatchStandaloneTestRunnerMessages;
diff --git a/Source/devtools/front_end/filteredItemSelectionDialog.css b/Source/devtools/front_end/filteredItemSelectionDialog.css
new file mode 100644
index 0000000..8154050
--- /dev/null
+++ b/Source/devtools/front_end/filteredItemSelectionDialog.css
@@ -0,0 +1,53 @@
+.filtered-item-list-dialog > input {
+    font-size: 11px;
+    width: 100%;
+    height: 24px;
+}
+
+.filtered-item-list-dialog > div.progress {
+    position: absolute;
+    top: 35px;
+    left: 10px;
+    right: 10px;
+    height: 2px;
+}
+
+.filtered-item-list-dialog > div.container {
+    position: absolute;
+    top: 38px;
+    bottom: 10px;
+    left: 10px;
+    right: 10px;
+    overflow-y: auto;
+    border: 1px solid rgb(187, 187, 187);
+    background-color: white;
+}
+
+.filtered-item-list-dialog-item {
+    padding: 2px;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    color: rgb(95, 95, 95);
+}
+
+.filtered-item-list-dialog-subtitle {
+    color: rgb(155, 155, 155);
+}
+
+.filtered-item-list-dialog-item.one-row .filtered-item-list-dialog-subtitle {
+    float: right;
+}
+
+.filtered-item-list-dialog-item.two-rows {
+    border-bottom: 1px solid rgb(235, 235, 235);
+}
+
+.filtered-item-list-dialog-item.selected {
+    background-color: rgb(224, 224, 224);
+}
+
+.filtered-item-list-dialog-item span.highlight {
+    color: #222;
+    font-weight: bold;
+}
diff --git a/Source/devtools/front_end/flameChart.css b/Source/devtools/front_end/flameChart.css
new file mode 100644
index 0000000..dacf7ad
--- /dev/null
+++ b/Source/devtools/front_end/flameChart.css
@@ -0,0 +1,31 @@
+.overview-container {
+    overflow: hidden;
+    position: absolute;
+    top: 0px;
+    width: 100%;
+    height: 80px;
+}
+
+.chart-container {
+    overflow: hidden;
+    position: absolute;
+    top: 80px;
+    width: 100%;
+    bottom: 0px;
+}
+
+#flame-chart-overview-grid .resources-dividers-label-bar {
+    pointer-events: auto;
+}
+
+#flame-chart-overview-container {
+    border-bottom: 1px solid rgba(0, 0, 0, 0.3);
+}
+
+.flame-chart-overview-canvas {
+    position: absolute;
+    top: 20px;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
diff --git a/Source/devtools/front_end/heapProfiler.css b/Source/devtools/front_end/heapProfiler.css
new file mode 100644
index 0000000..9870fb4
--- /dev/null
+++ b/Source/devtools/front_end/heapProfiler.css
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2010 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.
+ */
+
+.heap-snapshot-sidebar-tree-item .icon {
+    content: url(Images/profileIcon.png);
+}
+
+.heap-snapshot-sidebar-tree-item.wait .icon {
+    content: url(Images/spinnerActive.gif);
+}
+
+.heap-snapshot-sidebar-tree-item.wait.selected .icon {
+    content: url(Images/spinnerActiveSelected.gif);
+}
+
+body.inactive .heap-snapshot-sidebar-tree-item.wait .icon {
+    content: url(Images/spinnerInactive.gif);
+}
+
+body.inactive .heap-snapshot-sidebar-tree-item.wait.selected .icon {
+    content: url(Images/spinnerInactiveSelected.gif);
+}
+
+.heap-snapshot-sidebar-tree-item.small .icon {
+    content: url(Images/profileSmallIcon.png);
+}
+
+.heap-snapshot-view {
+    display: none;
+    overflow: hidden;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.heap-snapshot-view.visible {
+    display: block;
+}
+
+.heap-snapshot-view .view {
+    display: none;
+}
+
+.heap-snapshot-view .view.visible {
+    display: block;
+}
+
+.heap-snapshot-view .data-grid tr:empty {
+    height: 16px;
+    visibility: hidden;
+}
+
+.heap-snapshot-view .data-grid {
+    border: none;
+}
+
+.heap-snapshot-view .data-grid td.count-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid td.addedCount-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid td.removedCount-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid td.countDelta-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid td.addedSize-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid td.removedSize-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid td.sizeDelta-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid td.shallowSize-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid td.retainedSize-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid td.distanceToWindow-column {
+    text-align: right;
+}
+
+.heap-snapshot-view .data-grid div.heap-snapshot-multiple-values {
+    float: right;
+}
+
+.heap-snapshot-view .data-grid span.percent-column {
+    color: grey;
+    width: 32px;
+    float: right;
+}
+
+.heap-snapshot-view .console-formatted-object, .console-formatted-node {
+    display: inline;
+    position: static;
+}
+
+.detached-dom-tree-node {
+    background-color: #FF9999;
+}
+
+.heap-snapshot-view .console-formatted-string {
+    white-space: nowrap;
+}
+
+.heap-snapshot-view .console-formatted-id {
+    color: grey;
+}
+
+.heap-snapshot-view .data-grid tr.selected * {
+    color: inherit;
+}
+
+.heap-snapshot-view .data-grid:focus tr.selected * {
+    color: white;
+}
+
+.heap-snapshot-view .delimiter {
+    height: 24px;
+    background-color: #d6dde5;
+}
+
+.heap-snapshot-view .data-grid {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.heap-snapshot-view .views-container {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 173px;
+}
+
+.reserve-80px-at-top {
+    top: 80px !important;
+}
+
+.heap-snapshot-view .views-container .view {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.heap-snapshot-view .retaining-paths-view {
+    height: 150px;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+.heap-snapshot-view .class-view-grid {
+    top: 22px;
+}
+
+.heap-snapshot-view .class-view-toolbar {
+    height: 22px;
+    background-color: #DDD;
+    display: block;
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+}
+
+.heap-snapshot-view .class-view-toolbar input.class-name-filter {
+    width: 200px;
+    height: 18px;
+    font-size: 11px;
+    font-family: inherit;
+    padding: 2px;
+    margin: 2px 10px;
+    background-color: white;
+    border: solid 1px #BBB;
+}
+
+.heap-snapshot-view .retainers-view-header {
+    background-image: url(Images/statusbarResizerVertical.png), -webkit-linear-gradient(rgb(253,253,253), rgb(230,230,230) 75%, rgb(230,230,230));
+    border-top: 1px solid rgb(202, 202, 202);
+    background-repeat: no-repeat;
+    background-position: right center, center;
+    cursor: row-resize;
+    height: 23px;
+    display: block;
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 150px;
+}
+
+.heap-snapshot-view .retainers-view-header .title > span {
+    display: inline-block;
+    padding-top: 3px;
+    vertical-align: middle;
+    margin-left: 4px;
+    margin-right: 8px;
+}
+
+.heap-snapshot-view tr:not(.selected) td.object-column span.highlight {
+    background-color: rgb(255, 255, 200);
+}
+
+.heap-snapshot-view td.object-column span.grayed {
+    color: gray;
+}
+
+.heap-snapshot-help-status-bar-item .glyph {
+    -webkit-mask-position: -160px -2px;
+}
+
+table.heap-snapshot-help {
+    border-spacing: 12px 2px;
+}
+
+.cycled-ancessor-node {
+    opacity: 0.6;
+}
+
+#heap-recording-view .heap-snapshot-view {
+    top: 80px;
+}
+
+.overview-container {
+    overflow: hidden;
+    position: absolute;
+    top: 0px;
+    width: 100%;
+    height: 80px;
+}
+
+#heap-recording-overview-grid .resources-dividers-label-bar {
+    pointer-events: auto;
+}
+
+#heap-recording-overview-container {
+    border-bottom: 1px solid rgba(0, 0, 0, 0.3);
+}
+
+.heap-recording-overview-canvas {
+    position: absolute;
+    top: 20px;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
diff --git a/Source/devtools/front_end/helpScreen.css b/Source/devtools/front_end/helpScreen.css
new file mode 100644
index 0000000..6c30aa8
--- /dev/null
+++ b/Source/devtools/front_end/helpScreen.css
@@ -0,0 +1,477 @@
+.help-window-outer {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 2000;
+}
+
+.help-window-main {
+    max-height: 100%;
+    color: white;
+    background-color: rgba(17, 17, 17, 0.85);
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+    border-top-width: 0;
+    border-radius: 10px;
+}
+
+.help-window-caption {
+    border-bottom: solid 1px rgb(153, 153, 153);
+    margin: 0 8px;
+    padding: 0 2px;
+    line-height: 28px;
+}
+
+.help-window-title {
+    font-size: 16px;
+    margin: 0;
+    padding-top: 1px;
+    margin-bottom: -1px;
+}
+
+.help-content {
+    overflow-y: auto;
+    overflow-x: hidden;
+    scrollbar-width: 11px;
+    -webkit-flex: 1;
+    margin: 8px;
+    padding: 0 4px;
+    font-size: 13px;
+}
+
+.help-window-main ::-webkit-scrollbar {
+    width: 11px;
+}
+
+.help-window-main ::-webkit-scrollbar-corner,
+.help-window-main ::-webkit-resizer {
+    display: none;
+}
+
+.help-window-main ::-webkit-scrollbar-thumb:vertical {
+    background: -webkit-gradient(linear, left top, right top, from(rgb(128, 128, 128)), to(rgb(128, 128, 128)), color-stop(40%, rgb(96, 96, 96)));
+    border-radius: 5px;
+    min-height: 20px;
+}
+
+.help-window-main ::-webkit-scrollbar-thumb:vertical:hover,
+.help-window-main ::-webkit-scrollbar-thumb:vertical:active {
+    background: -webkit-gradient(linear, left top, right top, from(rgb(176, 176, 176)), to(rgb(176, 176, 176)), color-stop(40%, rgb(144, 144, 144)));
+}
+
+.help-window-main ::-webkit-scrollbar-track:vertical {
+    background: -webkit-gradient(linear, left top, right top, from(rgb(10, 10, 10)), to(rgb(32, 32, 32)), color-stop(25%, rgb(32, 32, 32)));
+    border-radius: 5px;
+}
+
+.help-close-button {
+    position: absolute;
+    top: 8px;
+    right: 8px;
+}
+
+body.dock-to-bottom .help-content {
+    margin-bottom: 8px;
+}
+
+body.platform-mac .help-window-main .tabbed-pane-header-contents {
+    margin-left: 27px;
+}
+
+body.platform-mac .help-window-main .help-window-title {
+    margin-left: 18px;
+}
+
+.help-container {
+    width: 100%;
+    -webkit-user-select: auto;
+    -webkit-column-width: 470px;
+}
+
+body.platform-mac .settings-tab.help-container {
+    -webkit-column-width: 430px;
+}
+
+body.platform-mac .help-container {
+    -webkit-column-width: 330px;
+}
+
+.help-block {
+    display: block;
+    padding-bottom: 9px;
+    width: 470px;
+    -webkit-column-break-inside: avoid;
+}
+
+body.platform-mac .settings-tab .help-block {
+    width: 430px;
+}
+
+#drawer-contents .settings-tab {
+    padding-left: 12px;
+}
+
+.settings-tab.help-container {
+    -webkit-column-width: 410px;
+}
+
+.settings-tab .help-block {
+    width: 410px;
+}
+
+.help-line {
+    padding-bottom: 3px;
+}
+
+.help-key-cell {
+    display: inline-block;
+    width: 270px;
+    text-align: right;
+}
+
+body.platform-mac .help-key-cell {
+    width: 120px;
+}
+
+.help-cell {
+    display: inline;
+}
+
+.help-section-title {
+    font-weight: bold;
+}
+
+.help-key {
+    font-weight: bold;
+}
+
+body.platform-mac .help-key {
+    font-family: Lucida Grande, sans-serif;
+}
+
+.help-combine-keys, .help-key-delimiter {
+    font-size: 9px;
+}
+
+.help-combine-keys {
+    margin: 0 0.3em;
+}
+
+.help-key-delimiter {
+    margin: 0 0.5em;
+}
+
+.help-content p {
+    margin: 3px 0;
+}
+
+.help-content fieldset {
+    border: none;
+    margin-left: 7px;
+}
+
+#general-tab-content .help-content fieldset legend {
+    font-size: 14px;
+}
+
+body.platform-mac .settings-tab.help-content fieldset {
+    margin-left: 10px;
+}
+
+.help-content fieldset p {
+    border-left: 1px solid rgb(128,128,128);
+    margin: 0 0 0 6px;
+    padding-left: 3px;
+}
+
+.help-content fieldset label {
+    padding-right: 4px;
+}
+
+.help-content p.help-section {
+    margin: 0 0 15px 0;
+}
+
+.settings-experiments-warning-subsection-warning {
+    color: rgb(200, 0, 0);
+}
+
+.settings-experiments-warning-subsection-message {
+    color: inherit;
+}
+
+#resolution-override-section {
+    margin-left: 13px;
+}
+
+.help-content input[type=checkbox] {
+    height: 13px;
+    width: 13px;
+    margin: 0 7px 0 0;
+    vertical-align: -2px;
+}
+
+body.platform-mac .help-content input[type=checkbox] {
+    vertical-align: -1px;
+}
+
+.help-content input[type=radio] {
+    vertical-align: -2px;
+}
+
+body.platform-mac .help-content input[type=radio] {
+    vertical-align: -1px;
+}
+
+.help-content select {
+    background-color: rgb(64, 64, 64);
+    color: white;
+    border-color: black;
+    padding: 0 4px;
+}
+
+.help-content select:disabled {
+    background-color: rgb(32, 32, 32);
+    color: graytext;
+}
+
+.help-content option {
+    background-color: #EEEEEE;
+    color: #222;
+}
+
+#settings-screen .help-window-main,
+#drawer-contents .help-window-main {
+    color: rgb(48, 57, 66);
+    background-color: white;
+    border-radius: 0;
+}
+
+#settings-screen .help-window-main {
+    position: absolute;
+    top: 10px;
+    right: 10px;
+    bottom: 10px;
+    left: 10px;
+    height: initial;
+    padding: 21px 0 0 21px;
+    box-shadow: 1px 1px 5px 2px rgba(128, 128, 128, 0.7);
+}
+
+#settings-screen .help-window-label {
+    font-size: 18px;
+    color: inherit;
+    padding-top: 1px;
+}
+
+#drawer-contents .help-container-wrapper {
+    top: 32px;
+}
+
+.help-container-wrapper {
+    position: absolute;
+    top: 60px;
+    left: 5px;
+    right: 0;
+    bottom: 0;
+    overflow: auto;
+}
+
+#settings-screen .tabbed-pane-header {
+    border: none transparent;
+    height: auto;
+    width: 110px;
+    position: relative;
+    top: 14px;
+}
+
+#settings-screen .tabbed-pane-header-contents {
+    margin: 0;
+}
+
+#settings-screen .tabbed-pane-header-tabs {
+    padding-top: 5px;
+    width: 100px;
+}
+
+#settings-screen .tabbed-pane-header-tab {
+    background-color: transparent;
+    border: none transparent;
+    cursor: pointer;
+    font-weight: normal;
+    text-shadow: none;
+    color: #999999;
+    height: 22px;
+    font-size: 13px;
+    padding-left: 0;
+}
+
+#settings-screen .tabbed-pane-header-tab.selected {
+    color: inherit;
+}
+
+#settings-screen .tabbed-pane-content,
+#drawer-contents .tabbed-pane-content {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    margin: 8px;
+}
+
+#drawer-contents .tabbed-pane-content {
+    padding: 0 4px;
+}
+
+#settings-screen .tabbed-pane-content {
+    left: 120px;
+}
+
+#drawer-contents .tabbed-pane-content {
+    left: 0;
+}
+
+.settings-tab.help-content {
+    margin: 0;
+    padding: 0;
+}
+
+.settings-tab input:not([type]), .settings-tab input[type="text"] {
+    border: 1px solid rgb(213, 213, 213);
+    border-radius: 2px;
+    color: #444444;
+    font: inherit;
+    padding: 3px;
+}
+
+.settings-tab input.numeric {
+    text-align: right;
+}
+
+.settings-tab-container header {
+    padding: 14px 0;
+    border-bottom: 1px solid #EEEEEE;
+}
+
+#drawer-contents .settings-tab-container header {
+    padding: 0;
+}
+
+#tab-shortcuts {
+    margin-top: 25px !important;
+}
+
+#experiments-tab-content .help-container {
+    -webkit-column-width: 470px;
+}
+
+#experiments-tab-content .help-block {
+    width: 470px;
+}
+
+.settings-tab-container header > h3 {
+    font-size: 18px;
+    font-weight: normal;
+    margin: 0;
+    padding-bottom: 3px;
+}
+
+.settings-tab .help-section-title {
+    color: #222;
+}
+
+.settings-tab .help-block label {
+    font-size: 14px;
+}
+
+.settings-tab .help-block fieldset:not(.toplevel) label {
+    font-size: 13px;
+}
+
+.settings-tab .help-block fieldset:disabled label:hover,
+.settings-tab .help-block fieldset.toplevel label:hover {
+    color: inherit;
+}
+
+.settings-tab .help-block label:hover {
+    color: #222;
+}
+
+.settings-tab p {
+    margin: 10px 0;
+}
+
+.settings-tab fieldset p {
+    border-left: none transparent;
+    padding: 2px 0 2px 3px;
+}
+
+.settings-tab select {
+    background-color: #FAFAFA;
+    border-color: rgb(213, 213, 213);
+    border-radius: 2px;
+    color: inherit;
+    padding: 0 4px;
+}
+
+.settings-tab select:disabled {
+    background-color: rgb(221, 221, 221);
+}
+
+.settings-tab .file-systems-editor input.file-system-path {
+    width: 383px;
+}
+
+.settings-tab .workspace-settings-row input {
+    width: 190px;
+}
+
+.settings-tab .file-mappings-editor .workspace-settings-row input.file-mapping-url {
+    margin-right: 3px;
+}
+
+#workspace-tab-content .button:hover {
+    opacity: 1.0 !important;
+}
+
+#workspace-tab-content .workspace-settings-row:hover .button {
+    visibility: visible;
+    opacity: 0.4;
+}
+
+#workspace-tab-content .workspace-settings-row .button {
+    width: 10px;
+    height: 10px;
+    border: none;
+    -webkit-appearance: none;
+    background-color: transparent;
+    visibility: hidden;
+    background-position: center;
+    margin: 0 0 0 4px;
+}
+
+#workspace-tab-content .workspace-settings-row .remove-button {
+    background-image: url(Images/deleteIcon.png);
+}
+
+#workspace-tab-content .workspace-settings-row .add-button {
+    background-image: url(Images/addIcon.png);
+}
+
+#workspace-tab-content .workspace-settings-row .file-system-add-button {
+    color: gray;
+    border: none;
+    border-bottom: 1px transparent solid;
+    -webkit-appearance: none;
+    background: transparent;
+}
+
+#workspace-tab-content .workspace-settings-row .file-system-add-button:hover {
+    border-bottom: 1px gray dashed;
+}
+
+#workspace-tab-content .workspace-settings-error {
+    color: red;
+}
diff --git a/Source/devtools/front_end/indexedDBViews.css b/Source/devtools/front_end/indexedDBViews.css
new file mode 100644
index 0000000..2dc038d
--- /dev/null
+++ b/Source/devtools/front_end/indexedDBViews.css
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+.indexed-db-database-view {
+    -webkit-user-select: text;
+    margin-top: 5px;
+}
+
+.indexed-db-database-view .outline-disclosure {
+    padding-left: 0;
+}
+
+.indexed-db-database-view .outline-disclosure li {
+    white-space: nowrap;
+}
+
+.indexed-db-database-view .outline-disclosure .attribute-name {
+    color: rgb(33%, 33%, 33%);
+    display: inline-block;
+    margin-right: 0.5em;
+    font-weight: bold;
+    vertical-align: top;
+}
+
+.indexed-db-database-view .outline-disclosure .attribute-value {
+    display: inline;
+    margin-top: 1px;
+}
+
+.indexed-db-data-view .data-view-toolbar {
+    position: relative;
+    margin-top: -1px;
+    height: 24px;
+}
+
+.indexed-db-data-view .data-view-toolbar .back-button img {
+    content: url(Images/back.png);
+}
+
+.indexed-db-data-view .data-view-toolbar .forward-button img {
+    content: url(Images/forward.png);
+}
+
+.indexed-db-data-view .data-view-toolbar .key-input {
+    font-size: 10px;
+    margin-top: 3px;
+    margin-left: 3px;
+    width: 200px;
+}
+
+.indexed-db-data-view .data-grid-container {
+    top: 23px;
+}
+
+.indexed-db-data-view .data-grid {
+    height: 100%;
+    border: 0;
+}
+
+.indexed-db-data-view .data-grid .data-container tr:nth-child(even) {
+    background-color: white;
+}
+
+.indexed-db-data-view .data-grid .data-container tr:nth-child(odd) {
+    background-color: #EAF3FF;
+}
+
+.indexed-db-data-view .data-grid .data-container tr:nth-last-child(1) {
+    background-color: white;
+}
+
+.indexed-db-data-view .data-grid .data-container tr:nth-last-child(1) td {
+    border: 0;
+}
+
+.indexed-db-data-view .data-grid .data-container td {
+    height: 18px;
+}
+
+.indexed-db-data-view .data-grid .data-container td.value-column,
+.indexed-db-data-view .data-grid .data-container td.key-column,
+.indexed-db-data-view .data-grid .data-container td.primaryKey-column {
+    padding: 0;
+}
+
+.indexed-db-data-view .data-grid .data-container td.value-column div.primitive-value,
+.indexed-db-data-view .data-grid .data-container td.key-column div.primitive-value,
+.indexed-db-data-view .data-grid .data-container td.primaryKey-column div.primitive-value {
+    padding-left: 5px;
+    margin-top: 1px;
+}
+
+.indexed-db-data-view .data-grid .data-container td .section .header .title {
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
diff --git a/Source/devtools/front_end/inspector.css b/Source/devtools/front_end/inspector.css
new file mode 100644
index 0000000..41ad59e
--- /dev/null
+++ b/Source/devtools/front_end/inspector.css
@@ -0,0 +1,3028 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+input[type="search"]:focus, input[type="text"]:focus {
+    outline: auto 5px -webkit-focus-ring-color;
+}
+
+.fill {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.inline-block {
+    display: inline-block;
+}
+
+.hidden {
+    display: none;
+}
+
+.nowrap {
+    white-space: nowrap !important;
+}
+
+#toolbar {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 26px;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(191, 191, 191)), to(rgb(151, 151, 151)));
+    padding-top: 1px;
+    padding-left: 5px;
+    border-bottom: 1px solid rgb(80, 80, 80);
+    -webkit-flex-direction: row;
+    -webkit-background-origin: padding;
+    -webkit-background-clip: padding;
+}
+
+body.show-toolbar-icons #toolbar {
+    height: 56px;
+}
+
+body.show-toolbar-icons.dock-to-bottom #toolbar {
+    height: 34px;
+}
+
+body.inactive #toolbar {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(233, 233, 233)), to(rgb(207, 207, 207)));
+    border-bottom: 1px solid rgb(64%, 64%, 64%);
+}
+
+body.dock-to-bottom #toolbar {
+    padding-top: 0;
+    border-top: 1px solid rgb(100, 100, 100);
+    cursor: default;
+}
+
+body.dock-to-bottom.platform-mac #toolbar {
+    border-top-color: white;
+}
+
+body.dock-to-bottom.inactive #toolbar {
+    border-top: 1px solid rgb(64%, 64%, 64%);
+}
+
+body.platform-windows #toolbar, body.platform-windows.inactive #toolbar {
+    background-image: none;
+}
+
+body.undocked.platform-mac-leopard #toolbar {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(175, 175, 175)), to(rgb(151, 151, 151))) !important;
+}
+
+body.undocked.platform-mac-leopard.inactive #toolbar {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(221, 221, 221)), to(rgb(207, 207, 207))) !important;
+}
+
+body.undocked.platform-mac-snowleopard #toolbar {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(189, 189, 189)), to(rgb(167, 167, 167))) !important;
+}
+
+body.undocked.platform-mac-snowleopard.inactive #toolbar {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(228, 228, 228)), to(rgb(216, 216, 216))) !important;
+}
+
+body.undocked.platform-mac-mountain-lion #toolbar {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(208, 208, 208)), to(rgb(200, 200, 200))) !important;
+}
+
+body.undocked.platform-mac-mountain-lion.inactive #toolbar {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(238, 238, 238)), to(rgb(224, 224, 224))) !important;
+}
+
+.toolbar-item {
+    display: inline-block;
+    float: left;
+    margin: 0;
+    padding-right: 6px;
+    background-color: transparent;
+    border-style: none;
+    border-color: transparent;
+    color: inherit;
+    font-family: inherit;
+    font-size: inherit;
+}
+
+body:not(.show-toolbar-icons) .toolbar-item {
+    height: 24px;
+}
+
+body.show-toolbar-icons .toolbar-item.toggleable {
+    padding-top: 2px;
+    padding-bottom: 2px;
+}
+
+body.show-toolbar-icons.dock-to-bottom .toolbar-item.toggleable {
+    margin: 2px 0;
+    padding-bottom: 2px;
+}
+
+.toolbar-item.toggleable.toggled-on {
+    border-width: 0 2px 0 2px;
+    padding-left: 4px;
+    padding-right: 4px;
+    -webkit-border-image: url(Images/toolbarItemSelected.png) 0 2 0 2;
+}
+
+.toolbar-icon {
+    display: none;
+    width: 32px;
+    height: 32px;
+    background-image: url(Images/toolbarIcons.png);
+    vertical-align: top;
+}
+
+body.show-toolbar-icons .toolbar-icon {
+    display: block;
+    margin: auto;
+}
+
+body.show-toolbar-icons.dock-to-bottom .toolbar-icon {
+    width: 24px;
+    height: 24px;
+    display: inline-block;
+    vertical-align: middle;
+    background-image: url(Images/toolbarIconsSmall.png);
+}
+
+body.dock-to-bottom .toolbar-icon.custom-toolbar-icon {
+    background-position-x: -32px;
+}
+
+.toolbar-item:active .toolbar-icon {
+    background-position-y: 32px;
+}
+
+body.dock-to-bottom .toolbar-item:active .toolbar-icon {
+    background-position-y: 24px;
+}
+
+.toolbar-label {
+    line-height: 22px;
+    text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0;
+    display: inline;
+}
+
+.toolbar-item.toggleable .close-button {
+    margin-left: 2px;
+    margin-right: -2px;
+    position: relative;
+    top: 2px;
+    opacity: 0.5;
+}
+
+.toolbar-item.toggleable .close-button:hover {
+    opacity: 1;
+}
+
+body.show-toolbar-icons.dock-to-bottom .toolbar-item.toggleable .close-button {
+    top: 3px;
+}
+
+#toolbar-dropdown .toolbar-items-separator {
+    border-bottom: 1px solid #aaa;
+    width: 100%;
+    margin: 5px 0;
+}
+
+body.show-toolbar-icons .toolbar-label {
+    line-height: 15px;
+}
+
+.toolbar-item.toggleable:active .toolbar-label {
+    text-shadow: none;
+}
+
+body.show-toolbar-icons.dock-to-bottom .toolbar-label {
+    display: inline-block;
+    margin-left: 3px;
+    top: 0;
+    vertical-align: middle;
+}
+
+body.dock-to-bottom #search-toolbar-label {
+    display: none;
+}
+
+#toolbar-controls {
+    float: right;
+    display: -webkit-flex;
+    -webkit-align-items: center;
+    height: 100%;
+}
+
+#toolbar-dropdown-arrow {
+    font-size: 14px;
+    font-weight: bold;
+    border: 0;
+    background-color: transparent;
+    -webkit-border-radius: 5px;
+    text-shadow: none;
+    cursor: default;
+    margin: 0;
+    /* A line height of 0 allows precise text positioning using padding. */
+    line-height: 0;
+    padding: 11px 6px 11px;
+}
+
+#toolbar-dropdown-arrow.dropdown-visible {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(251, 251, 251, 0.9)), to(rgba(231, 231, 231, 0.9)));
+}
+
+#toolbar-dropdown-arrow:hover {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(191, 191, 191, 0.7)), to(rgba(171, 171, 171, 0.5)));
+}
+
+#toolbar-dropdown-arrow:active {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(111, 111, 111, 0.8)), to(rgba(91, 91, 91, 0.8)));
+}
+
+#toolbar-dropdown {
+    position: absolute;
+    z-index: 1000;
+    -webkit-box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.4);
+    border: 1px solid rgb(128, 128, 128);
+    background-color: inherit;
+    background-image: inherit;
+}
+
+body.show-toolbar-icons #toolbar-dropdown {
+    padding: 4px;
+}
+
+body.undocked.platform-mac-leopard #toolbar-dropdown,
+body.undocked.platform-mac-snowleopard #toolbar-dropdown {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(191, 191, 191)), to(rgb(151, 151, 151)));
+}
+
+#toolbar-dropdown .scrollable-content {
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+    -webkit-align-items: flex-start;
+    padding-right: 0;
+}
+
+#toolbar-dropdown .toolbar-item {
+    display: -webkit-flex;
+    -webkit-flex-direction: row;
+    width: 100%;
+    border: 1px solid rgba(0, 0, 0, 0);
+}
+
+body.show-toolbar-icons #toolbar-dropdown .toolbar-item {
+    position: relative;
+    left: -2px;
+    margin: 0px 2px;
+    padding: 4px;
+}
+
+#toolbar-dropdown .toolbar-item.toggleable.toggled-on {
+    border: 1px solid rgba(100, 100, 120, 0.4);
+    -webkit-border-image: none;
+    background: -webkit-gradient(linear, left top, left bottom, from(rgba(128, 128, 128, 0.6)), to(rgba(128, 128, 128, 0.6)), color-stop(20%, rgba(158, 158, 158, 0.2)), color-stop(80%, rgba(158, 158, 158, 0.2)));
+}
+
+#toolbar-dropdown .toolbar-item.toggleable:hover {
+    background: -webkit-gradient(linear, left top, left bottom, from(rgba(128, 128, 128, 0.6)), to(rgba(128, 128, 128, 0.3)), color-stop(20%, rgba(158, 158, 158, 0.2)), color-stop(80%, rgba(158, 158, 158, 0.1)));
+}
+
+#toolbar-dropdown .toolbar-icon {
+    margin-left: 0;
+    margin-right: 0.5em;
+}
+
+#toolbar-dropdown .toolbar-label {
+    line-height: 22px;
+    top: 0;
+}
+
+body.show-toolbar-icons #toolbar-dropdown .toolbar-label {
+    line-height: 32px;
+}
+
+#toolbar-panels-menu {
+    border: 0;
+    -webkit-border-radius: 5px;
+    cursor: default;
+    font-family: Arial, Helvetica, sans-serif !important;
+    font-weight: bold;
+    line-height: 16px;
+    height: 22px;
+    padding: 0 4px 0 4px;
+    margin: 1px 3px;
+}
+
+body.show-toolbar-icons #toolbar-panels-menu {
+    margin: 16px 3px;
+}
+
+body.show-toolbar-icons.dock-to-bottom #toolbar-panels-menu {
+    margin: 6px 3px;
+}
+
+#toolbar-panels-menu:hover:not(.disabled) {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(191, 191, 191, 0.7)), to(rgba(171, 171, 171, 0.5)));
+}
+
+#toolbar-panels-menu.disabled {
+    opacity: 0.4;
+    text-shadow: white 1px 1px 0;
+}
+
+#toolbar-panels-menu:active:not(.disabled) {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(111, 111, 111, 0.8)), to(rgba(91, 91, 91, 0.8)));
+}
+
+.scrollable-content {
+    position: static;
+    height: 100%;
+    overflow-y: auto;
+    width: 100%;
+    margin-right: 12px;
+    padding-right: 3px;
+}
+
+.scrollable-content::-webkit-scrollbar {
+    width: 11px;
+}
+
+.scrollable-content::-webkit-scrollbar-corner,
+.scrollable-content::-webkit-resizer {
+    display: none;
+}
+
+.scrollable-content::-webkit-scrollbar-thumb:vertical {
+    background: -webkit-gradient(linear, left top, right top, from(rgb(192, 192, 192)), to(rgb(192, 192, 192)), color-stop(40%, rgb(214, 214, 214)));
+    border-radius: 5px;
+    min-height: 20px;
+}
+
+.scrollable-content::-webkit-scrollbar-thumb:vertical:hover,
+.scrollable-content::-webkit-scrollbar-thumb:vertical:active {
+    background: -webkit-gradient(linear, left top, right top, from(rgb(230, 230, 230)), to(rgb(230, 230, 230)), color-stop(40%, rgb(252, 252, 252)));
+}
+
+.scrollable-content::-webkit-scrollbar-track:vertical {
+    background: -webkit-gradient(linear, left top, right top, from(rgb(128, 128, 128)), to(rgb(164, 164, 164)), color-stop(25%, rgb(164, 164, 164)));
+    border-radius: 5px;
+}
+
+.search-replace {
+    -webkit-appearance: none;
+    border: 0;
+    padding: 0 2px;
+    margin: 0;
+    width: 165px;
+}
+
+.filter {
+    -webkit-appearance: none;
+    border: 0;
+    padding: 0 2px;
+    margin: 0;
+    width: 251px;
+}
+
+.search-replace:focus {
+    outline: none;
+}
+
+.toolbar-search {
+    border-spacing: 1px;
+}
+
+.toolbar-search td {
+    padding: 0 5px 0 0;
+}
+
+.toolbar-search-navigation-controls {
+    position: absolute;
+    top: 0;
+    right: 0;
+    height: 18px;
+    background-image: -webkit-linear-gradient(rgb(228, 228, 228), rgb(206, 206, 206));
+}
+
+.toolbar-search-navigation {
+    display: inline-block;
+    width: 18px;
+    height: 18px;
+    background-repeat: no-repeat;
+    background-position: 4px 7px;
+    border-left: 1px solid rgb(170, 170, 170);
+    opacity: 0.3;
+}
+
+.toolbar-search-navigation.enabled {
+    opacity: 1.0;
+}
+
+.toolbar-search input[type="checkbox"] {
+    position: relative;
+    margin-top: -1px;
+    margin-left: 15px;
+    top: 2px;
+}
+
+.toolbar-search button {
+    border: 1px solid rgb(163, 163, 163);
+    border-radius: 8px;
+    margin: 0 0px;
+    font-size: 11px;
+    background-image: -webkit-linear-gradient(rgb(241, 241, 241), rgb(220, 220, 220));
+    width: 100%;
+}
+
+.toolbar-search button:active {
+    background-image: -webkit-linear-gradient(rgb(185, 185, 185), rgb(156, 156, 156));
+}
+
+.toolbar-search-control {
+    display: inline-block;
+    position: relative;
+    background-color: white;
+    border: 1px solid rgb(163, 163, 163);
+    height: 20px;
+    border-radius: 2px;
+    padding-top: 1px;
+}
+
+.toolbar-replace-control {
+    border: 1px solid rgb(163, 163, 163);
+    height: 20px;
+    border-radius: 2px;
+    width: 100%;
+}
+
+.toolbar-search-navigation.enabled:active {
+    background-position: 4px 7px, 0px 0px;
+}
+
+.toolbar-search-navigation.toolbar-search-navigation-prev {
+    background-image: url(Images/searchPrev.png);
+    border-left: 1px solid rgb(163, 163, 163);
+}
+
+.toolbar-search-navigation.toolbar-search-navigation-prev.enabled:active {
+    background-image: url(Images/searchPrev.png), -webkit-linear-gradient(rgb(168, 168, 168), rgb(116, 116, 116));
+}
+
+.toolbar-search-navigation.toolbar-search-navigation-next {
+    background-image: url(Images/searchNext.png);
+    border-left: 1px solid rgb(230, 230, 230);
+}
+
+.toolbar-search-navigation.toolbar-search-navigation-next.enabled:active {
+    background-image: url(Images/searchNext.png), -webkit-linear-gradient(rgb(168, 168, 168), rgb(116, 116, 116));
+}
+
+.search-results-matches {
+    display: inline-block;
+    min-width: 50px;
+    min-height: 10px;
+    margin-right: 36px;
+    text-align: right;
+    font-size: 11px;
+    padding: 0 4px;
+    color: rgb(165, 165, 165);
+}
+
+.toolbar-item.elements .toolbar-icon {
+    background-position-x: 0;
+}
+
+.toolbar-item.resources .toolbar-icon {
+    background-position-x: -32px;
+}
+
+body.dock-to-bottom .toolbar-item.resources .toolbar-icon {
+    background-position-x: -24px;
+}
+
+.toolbar-item.network .toolbar-icon {
+    background-position-x: -64px;
+}
+
+body.dock-to-bottom .toolbar-item.network .toolbar-icon {
+    background-position-x: -48px;
+}
+
+.toolbar-item.scripts .toolbar-icon {
+    background-position-x: -96px;
+}
+
+body.dock-to-bottom .toolbar-item.scripts .toolbar-icon {
+    background-position-x: -72px;
+}
+
+.toolbar-item.timeline .toolbar-icon {
+    background-position-x: -128px;
+}
+
+body.dock-to-bottom .toolbar-item.timeline .toolbar-icon {
+    background-position-x: -96px;
+}
+
+.toolbar-item.profiles .toolbar-icon {
+    background-position-x: -160px;
+}
+
+.toolbar-item.cpu-profiler .toolbar-icon {
+    background-position-x: -160px;
+}
+
+.toolbar-item.css-profiler .toolbar-icon {
+    background-position-x: -160px;
+}
+
+.toolbar-item.heap-profiler .toolbar-icon {
+    background-position-x: -160px;
+}
+
+.toolbar-item.canvas-profiler .toolbar-icon {
+    background-position-x: -160px;
+}
+
+.toolbar-item.memory-chart-profiler .toolbar-icon {
+    background-position-x: -160px;
+}
+
+.toolbar-item.memory-snapshot-profiler .toolbar-icon {
+    background-position-x: -160px;
+}
+
+body.dock-to-bottom .toolbar-item.profiles .toolbar-icon {
+    background-position-x: -120px;
+}
+
+.toolbar-item.audits .toolbar-icon {
+    background-position-x: -192px;
+}
+
+body.dock-to-bottom .toolbar-item.audits .toolbar-icon {
+    background-position-x: -144px;
+}
+
+.toolbar-item.console .toolbar-icon {
+    background-position-x: -224px;
+}
+
+body.dock-to-bottom .toolbar-item.console .toolbar-icon {
+    background-position-x: -168px;
+}
+
+.close-button, .close-button-gray {
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    display: inline-block;
+}
+
+.close-button {
+    width: 14px;
+    height: 14px;
+    background-position: -128px -216px;
+}
+
+.close-button-gray {
+    width: 13px;
+    height: 13px;
+    background-position: -175px -216px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.close-button, .close-button-gray {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.close-button:hover {
+    background-position: -96px -216px;
+}
+
+.close-button:active {
+    background-position: -111px -216px;
+}
+
+.close-button-gray:hover {
+    background-position: -143px -216px;
+}
+
+.close-button-gray:active {
+    background-position: -160px -216px;
+}
+
+
+.close-left {
+    float: left;
+}
+
+body.undocked .toolbar-item.close-left, body.undocked .toolbar-item.close-right {
+    display: none;
+}
+
+body.platform-mac .toolbar-item.close-right {
+    display: none;
+}
+
+body.remote .toolbar-item.close-left, body.remote .toolbar-item.close-right {
+    display: none;
+}
+
+body:not(.platform-mac) .toolbar-item.close-left {
+    display: none;
+}
+
+.toolbar-item.close-left, .toolbar-item.close-right {
+  display: -webkit-flex;
+  -webkit-align-items: center;
+  height: 100%;
+  cursor: default;
+}
+
+#main {
+    position: absolute;
+    z-index: 1;
+    top: 26px;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow: hidden;
+    background-color: white;
+}
+
+.animate-slow * {
+    -webkit-transition-duration: 2.5s !important;
+}
+
+.animate #main {
+    -webkit-transition: bottom 100ms linear;
+}
+
+body.show-toolbar-icons #main {
+    top: 56px;
+}
+
+body.show-toolbar-icons.dock-to-bottom #main {
+    top: 34px;
+}
+
+#main-panels {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 23px;
+    overflow: hidden;
+}
+
+body.drawer-visible #main-panels {
+    bottom: 24px;
+}
+
+#main-status-bar {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+.status-bar {
+    position: relative;
+    white-space: nowrap;
+    height: 23px;
+    overflow: hidden;
+    width: 100%;
+    z-index: 12;
+    background-image: -webkit-linear-gradient(rgb(243,243,243), rgb(235,235,235));
+    border-top: 1px solid rgb(202, 202, 202);
+    display: -webkit-flex;
+}
+
+.status-bar > div {
+    display: inline-block;
+    vertical-align: top;
+    overflow: visible;
+}
+
+.status-bar-item {
+    display: inline-block;
+    pointer-events: auto;
+    cursor: default;
+    height: 22px;
+    padding: 0;
+    margin-left: -1px;
+    margin-right: 0;
+    vertical-align: top;
+    border: 0 transparent none;
+    background-color: transparent;
+}
+
+.status-bar-text {
+    padding-left: 5px;
+    padding-right: 15px;
+    padding-top: 3px;
+}
+
+#floating-status-bar-container {
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    display: none;
+    border-bottom: 1px solid rgb(202, 202, 202);
+    cursor: ns-resize;
+    height: 24px;
+}
+
+.animate #floating-status-bar-container {
+    -webkit-transition-property: padding-left, bottom;
+    -webkit-transition-duration: 100ms;
+    -webkit-transition-timing-function: linear;
+}
+
+body.drawer-overlay #floating-status-bar-container {
+    box-shadow: 1px 1px 5px 2px rgba(128, 128, 128, 0.7);
+}
+
+body.drawer-visible #floating-status-bar-container {
+    display: -webkit-flex;
+}
+
+#floating-status-bar-resizer {
+    content: url(Images/statusbarResizerVertical.png);
+    margin-top: 7px;
+    pointer-events: none;
+    height: 8px;
+}
+
+#panel-status-bar {
+    -webkit-flex: 1 0;
+    display: -webkit-flex;
+    pointer-events: none;
+}
+
+#drawer-view-anchor {
+    display: inline-block;
+}
+
+.status-bar-item:active {
+    position: relative;
+    z-index: 200;
+}
+
+.glyph {
+    position: absolute;
+    top: -1px;
+    bottom: 1px;
+    left: 0;
+    right: 0;
+    background-color: rgba(0, 0, 0, 0.75);
+    z-index: 1;
+}
+
+.glyph.shadow {
+    top: 0;
+    bottom: 0;
+    background-color: white !important;
+    z-index: 0;
+}
+
+.long-click-glyph {
+    background-color: rgba(0, 0, 0, 0.75);
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs.png);
+    -webkit-mask-position: -288px -48px;
+    -webkit-mask-size: 320px 120px;
+    z-index: 1;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.long-click-glyph {
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.long-click-glyph.shadow {
+    top: 1px;
+    background-color: white !important;
+    z-index: 0;
+}
+
+button.status-bar-item {
+    position: relative;
+    width: 32px;
+    border-left: 1px solid rgb(202, 202, 202);
+    border-right: 1px solid rgb(202, 202, 202);
+}
+
+.status-bar button.status-bar-item .glyph {
+    margin: 0 -1px;
+}
+
+.status-bar select.status-bar-item:active,
+.status-bar button.status-bar-item:active {
+    border-left: 1px solid rgb(120, 120, 120);
+    border-right: 1px solid rgb(120, 120, 120);
+}
+
+button.status-bar-item .glyph.shadow {
+    background-color: rgba(255, 255, 255, 0.33) !important;
+}
+
+button.status-bar-item.toggled-on .glyph {
+    background-color: rgb(66, 129, 235);
+}
+
+button.status-bar-item:disabled {
+    opacity: 0.5;
+    background-position: 0 0 !important;
+}
+
+button.status-bar-item.extension {
+    background-image: none;
+    background-color: auto;
+}
+
+.status-bar-select-container {
+    display: inline-block;
+}
+
+.status-bar-select-arrow {
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    opacity: 0.7;
+    width: 10px;
+    height: 10px;
+    background-position: -20px -96px;
+    display: inline-block;
+    pointer-events: none;
+    position: relative;
+    top: 3px;
+    left: -3px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.status-bar-select-arrow {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+select.status-bar-item {
+    min-width: 48px;
+    color: rgb(48, 48, 48);
+    text-shadow: rgba(255, 255, 255, 0.75) 0 1px 0;
+    -webkit-appearance: none;
+    border: 0;
+    border-radius: 0;
+    padding: 0 15px 0 5px;
+    margin-right: -10px;
+    position: relative;
+    line-height: 20px;
+    font-family: inherit;
+    font-size: inherit;
+}
+
+select.status-bar-item, select.status-bar-item:hover {
+    border-left: 1px solid rgb(202, 202, 202);
+    border-right: 1px solid rgb(202, 202, 202);
+}
+
+.status-bar-item > .glyph {
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs.png);
+    -webkit-mask-size: 320px 120px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.status-bar-item > .glyph {
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+button.dock-status-bar-item.status-bar-item.toggled-undock .glyph {
+    -webkit-mask-position: 0 -48px;
+}
+
+button.dock-status-bar-item.status-bar-item.toggled-bottom .glyph {
+    -webkit-mask-position: -32px -24px;
+    background-color: rgba(0, 0, 0, 0.75);
+}
+
+button.dock-status-bar-item.status-bar-item.toggled-right .glyph {
+    -webkit-mask-position: -256px -48px;
+    background-color: rgba(0, 0, 0, 0.75);
+}
+
+body.undocked .alternate-status-bar-buttons-bar {
+    margin-left: 1px;
+}
+
+.alternate-status-bar-buttons-bar {
+    position: absolute;
+    width: 31px;
+    bottom: -3px;
+    background: white;
+}
+
+.alternate-status-bar-buttons-bar .status-bar-item {
+    height: 24px;
+    margin-top: -1px;
+    border: 1px solid rgb(202, 202, 202);
+    border-bottom: 1px solid transparent;
+}
+
+.alternate-status-bar-buttons-bar .status-bar-item.emulate-active {
+    background-color: rgb(163,163,163);
+    border: 1px solid rgb(120, 120, 120);
+    border-bottom: 1px solid transparent;
+}
+
+button.status-bar-item.settings-status-bar-item,
+button.status-bar-item.settings-status-bar-item:active {
+    border-right: 0 transparent none;
+}
+
+.settings-status-bar-item .glyph {
+    -webkit-mask-position: -160px -24px;
+}
+
+body.remote .dock-status-bar-item {
+    display: none;
+}
+
+.console-status-bar-item .glyph {
+    -webkit-mask-position: -64px -24px;
+}
+
+.clear-status-bar-item .glyph {
+    -webkit-mask-position: -64px 0;
+}
+
+.error-icon-small, .warning-icon-small, .red-ball, .green-ball, .orange-ball {
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    width: 10px;
+    height: 10px;
+    display: inline-block;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.error-icon-small, .warning-icon-small, .red-ball, .green-ball, .orange-ball {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.error-icon-small {
+    background-position: -213px -96px;
+}
+
+.warning-icon-small {
+    background-position: -202px -107px;
+}
+
+.red-ball {
+    background-position: -224px -96px;
+}
+
+.green-ball {
+    background-position: -235px -96px;
+}
+
+.orange-ball {
+    background-position: -246px -96px;
+}
+
+#error-warning-count {
+    padding: 4px 6px 6px 0px;
+    font-size: 11px;
+    height: 19px;
+    cursor: pointer;
+    line-height: 14px;
+}
+
+#error-warning-count:hover {
+    border-bottom: 1px solid rgb(96, 96, 96);
+}
+
+#error-warning-count .error-icon-small, #error-warning-count .warning-icon-small {
+    vertical-align: -1px;
+    margin-right: 2px;
+}
+
+#error-warning-count .warning-icon-small {
+    margin-left: 6px;
+}
+
+#drawer {
+    display: none;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    height: 200px;
+    background-color: white;
+    z-index: 1;
+}
+
+.animate #drawer {
+    -webkit-transition: height 100ms linear;
+}
+
+#drawer-contents {
+    position: absolute;
+    top: 0;
+    bottom: 24px;
+    left: 0;
+    right: 0;
+}
+
+body.drawer-visible #drawer {
+    display: block;
+}
+
+body.platform-mac .monospace, body.platform-mac .source-code {
+    font-size: 11px !important;
+    font-family: Menlo, monospace;
+}
+
+body.platform-mac.platform-mac-tiger .monospace,
+body.platform-mac.platform-mac-tiger .source-code {
+    font-size: 10px !important;
+    font-family: Monaco, monospace;
+}
+
+body.platform-windows .monospace, body.platform-windows .source-code {
+    font-size: 12px !important;
+    font-family: Consolas, Lucida Console, monospace;
+}
+
+body.platform-linux .monospace, body.platform-linux .source-code {
+    font-size: 11px !important;
+    font-family: dejavu sans mono, monospace;
+}
+
+#console-view {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    overflow-y: auto;
+}
+
+#console-messages {
+    position: absolute;
+    z-index: 0;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    padding: 2px 0;
+    overflow-y: overlay;
+    word-wrap: break-word;
+    -webkit-user-select: text;
+}
+
+#console-prompt {
+    clear: right;
+    position: relative;
+    border-top: 1px solid rgb(240, 240, 240);
+    padding: 1px 22px 1px 0px;
+    margin-left: 24px;
+    min-height: 16px;
+    white-space: pre-wrap;
+    -webkit-user-modify: read-write-plaintext-only;
+}
+
+#console-prompt::before {
+    background-position: -192px -96px;
+}
+
+.console-user-command-result.console-log-level::before {
+    background-position: -202px -96px;
+}
+
+.console-message, .console-user-command {
+    clear: right;
+    position: relative;
+    border-top: 1px solid rgb(240, 240, 240);
+    padding: 1px 22px 1px 0px;
+    margin-left: 24px;
+    min-height: 16px;
+}
+
+.console-mesage:first-child {
+    border-top: none;
+}
+
+.console-adjacent-user-command-result {
+    border-bottom: none;
+}
+
+.console-adjacent-user-command-result + .console-user-command-result.console-log-level::before {
+    background-image: none;
+}
+
+.console-message::before, .console-user-command::before, #console-prompt::before, .console-group-title::before {
+    position: absolute;
+    display: block;
+    content: "";
+    left: -17px;
+    top: 0.8em;
+    width: 10px;
+    height: 10px;
+    margin-top: -6px;
+    -webkit-user-select: none;
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.console-message::before, .console-user-command::before, #console-prompt::before, .console-group-title::before {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.console-message > .outline-disclosure li.parent::before {
+    top: 0;
+}
+
+.console-message .bubble {
+    display: inline-block;
+    height: 14px;
+    background-color: rgb(128, 151, 189);
+    vertical-align: middle;
+    white-space: nowrap;
+    padding: 1px 4px;
+    margin-top: -1px;
+    margin-right: 4px;
+    margin-left: -18px;
+    text-align: left;
+    font-size: 11px;
+    line-height: normal;
+    font-weight: bold;
+    text-shadow: none;
+    color: white;
+    -webkit-border-radius: 7px;
+}
+
+.console-message-text {
+    white-space: pre-wrap;
+}
+
+.repeated-message.console-error-level::before, .repeated-message.console-warning-level:before, .repeated-message.console-debug-level:before {
+    visibility: hidden;
+}
+
+.repeated-message .outline-disclosure, .repeated-message > .console-message-text {
+    -webkit-flex: 1;
+}
+
+.console-info {
+    color: rgb(128, 128, 128);
+    font-style: italic;
+}
+
+.console-group .console-group > .console-group-messages {
+    margin-left: 16px;
+}
+
+.console-group-title {
+    font-weight: bold;
+}
+
+.console-group-title::before {
+    -webkit-user-select: none;
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs.png);
+    -webkit-mask-size: 320px 120px;
+    float: left;
+    width: 8px;
+    content: "a";
+    color: transparent;
+    text-shadow: none;
+    margin-left: 3px;
+    margin-top: -7px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.console-group-title::before {
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.console-group .console-group-title::before {
+    -webkit-mask-position: -20px -96px;
+    background-color: rgb(110, 110, 110);
+}
+
+.console-group.collapsed .console-group-title::before {
+    -webkit-mask-position: -4px -96px;
+}
+
+.console-group.collapsed > .console-group-messages {
+    display: none;
+}
+
+.console-group {
+    position: relative;
+}
+
+.console-group-bracket {
+    position:absolute;
+    top: 15px;
+    left: 13px;
+    bottom: 5px;
+    width: 3px;
+    border-style: solid;
+    border-color: #A3A3A3;
+    border-width: 0px 0px 1px 1px;
+}
+
+.console-group.collapsed > .console-group-bracket {
+    display: none;
+}
+
+.console-error-level .console-message-text, .console-error-level .section > .header .title {
+    color: red !important;
+}
+
+.console-debug-level .console-message-text {
+    color: blue;
+}
+
+.console-error-level::before, .console-warning-level::before, .console-debug-level::before {
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    width: 10px;
+    height: 10px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.console-error-level::before, .console-warning-level::before, .console-debug-level::before {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.console-warning-level::before {
+    background-position: -202px -107px;
+}
+
+.console-error-level::before {
+    background-position: -213px -96px;
+}
+
+.console-user-command .console-message {
+    margin-left: -24px;
+    padding-right: 0;
+    border-bottom: none;
+}
+
+.console-user-command::before {
+    background-position: -192px -107px;
+}
+
+.console-user-command > .console-message-text {
+    color: rgb(0, 128, 255);
+}
+
+#console-messages a {
+    color: rgb(33%, 33%, 33%);
+    cursor: pointer;
+}
+
+#console-messages a:hover {
+    color: rgb(15%, 15%, 15%);
+}
+
+ol.watch-expressions > li.hovered {
+    background-color: #F0F0F0;
+}
+
+.console-message-url {
+    float: right;
+    text-align: right;
+    max-width: 100%;
+    margin-left: 4px;
+}
+
+.console-group-messages .section {
+    margin: 0 0 0 12px !important;
+}
+
+.console-group-messages .section > .header {
+    padding: 0 8px 0 0;
+    background-image: none;
+    border: none;
+    min-height: 0;
+}
+
+.console-group-messages .section > .header::before {
+    margin-left: -12px;
+}
+
+.console-group-messages .section > .header .title {
+    color: #222;
+    font-weight: normal;
+    line-height: 13px;
+}
+
+.console-group-messages .section .properties li .info {
+    padding-top: 0;
+    padding-bottom: 0;
+    color: rgb(60%, 60%, 60%);
+}
+
+.console-group-messages .outline-disclosure {
+    padding-left: 0;
+}
+
+.console-group-messages .outline-disclosure > ol {
+    padding: 0 0 0 12px !important;
+}
+
+.console-group-messages .outline-disclosure, .console-group-messages .outline-disclosure ol {
+    font-size: inherit;
+    line-height: 12px;
+}
+
+.console-group-messages .outline-disclosure.single-node li {
+    padding-left: 2px;
+}
+
+.console-group-messages .outline-disclosure li .selection {
+    margin-left: -6px;
+    margin-right: -6px;
+}
+
+.console-group-messages .add-attribute {
+    display: none;
+}
+
+.console-formatted-object, .console-formatted-node, .console-formatted-array {
+    position: relative;
+    display: inline-block;
+    vertical-align: top;
+    color: #222;
+}
+
+.console-formatted-node:hover {
+    background-color: rgba(56, 121, 217, 0.1);
+}
+
+.console-formatted-object .section, .console-formatted-node .section, .console-formatted-array .section {
+    position: static;
+}
+
+.console-formatted-object .section > .header::before {
+    margin-top: 0;
+}
+
+.console-formatted-object .properties, .console-formatted-node .properties {
+    padding-left: 0 !important;
+}
+
+.console-formatted-number {
+    color: rgb(28, 0, 207);
+}
+
+.console-formatted-string, .console-formatted-regexp {
+    color: rgb(196, 26, 22);
+    white-space: pre;
+}
+
+.console-formatted-null, .console-formatted-undefined {
+    color: rgb(128, 128, 128);
+}
+
+.console-formatted-preview-node,
+.section .console-formatted-node {
+    color: rgb(136, 18, 128);
+}
+
+.console-object-preview {
+    font-style: italic;
+}
+
+.object-info-state-note {
+    display: none;
+    width: 11px;
+    height: 11px;
+    background-color: rgb(179, 203, 247);
+    color: white;
+    text-align: center;
+    border-radius: 3px;
+    line-height: 13px;
+    margin: 0 6px;
+    font-size: 9px;
+}
+
+.object-info-state-note::before {
+    content: "i";
+}
+
+.section.expanded .object-info-state-note {
+    display: inline-block;
+}
+
+.error-message {
+    color: red;
+}
+
+.error-input {
+    background-color: rgb(220, 130, 130);
+}
+
+.auto-complete-text, .editing .auto-complete-text {
+    color: rgb(128, 128, 128) !important;
+    -webkit-user-select: none;
+    -webkit-user-modify: read-only;
+}
+
+.panel {
+    display: none;
+    overflow: hidden;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.panel.visible {
+    display: block;
+}
+
+iframe.extension {
+    width: 100%;
+    height: 100%;
+}
+
+iframe.panel.extension {
+    display: block;
+    height: 100%;
+}
+
+.webkit-line-gutter-backdrop {
+    /* Keep this in sync with view-source.css (.webkit-line-gutter-backdrop) */
+    width: 31px;
+    background-color: rgb(240, 240, 240);
+    border-right: 1px solid rgb(187, 187, 187);
+    position: absolute;
+    z-index: -1;
+    left: 0;
+    top: 0;
+    height: 100%
+}
+
+.outline-disclosure li.hovered:not(.selected) .selection {
+    display: block;
+    left: 3px;
+    right: 3px;
+    background-color: rgba(56, 121, 217, 0.1);
+    -webkit-border-radius: 5px;
+}
+
+.outline-disclosure li.highlighted .highlight {
+    background-color: rgb(255, 230, 179);
+    -webkit-border-radius: 4px;
+    padding-bottom: 2px;
+    margin-bottom: -2px;
+}
+
+.outline-disclosure li.selected.highlighted .highlight {
+    background-color: transparent;
+    padding-bottom: 0;
+    margin-bottom: 0;
+}
+
+.outline-disclosure li .selection {
+    display: none;
+    position: absolute;
+    left: 0;
+    right: 0;
+    height: 15px;
+    z-index: -1;
+}
+
+.outline-disclosure li.selected .selection {
+    display: block;
+    background-color: rgb(212, 212, 212);
+}
+
+.outline-disclosure li.elements-drag-over .selection {
+    display: block;
+    margin-top: -2px;
+    border-top: 2px solid rgb(56, 121, 217);
+}
+
+.outline-disclosure ol:focus li.selected .selection {
+    background-color: rgb(56, 121, 217);
+}
+
+.outline-disclosure ol:focus li.parent.selected::before {
+    background-color: white;
+}
+
+.outline-disclosure > ol {
+    position: relative;
+    padding: 2px 6px !important;
+    margin: 0;
+    cursor: default;
+    min-width: 100%;
+}
+
+.outline-disclosure, .outline-disclosure ol {
+    list-style-type: none;
+    -webkit-padding-start: 12px;
+    margin: 0;
+}
+
+.source-code {
+    font-family: monospace;
+    font-size: 11px !important;
+    white-space: pre-wrap;
+}
+
+.outline-disclosure li {
+    padding: 0 0 0 14px;
+    margin-top: 1px;
+    margin-left: -2px;
+    word-wrap: break-word;
+}
+
+.outline-disclosure ol:focus li.selected {
+    color: white;
+}
+
+.outline-disclosure ol:focus li.selected * {
+    color: inherit;
+}
+
+.outline-disclosure li.parent {
+    margin-left: -12px
+}
+
+.outline-disclosure li .webkit-html-tag.close {
+    margin-left: -12px;
+}
+
+.webkit-html-tag.shadow, .webkit-html-fragment.shadow {
+    opacity: 0.6;
+}
+
+.outline-disclosure li.parent::before {
+    float: left;
+    width: 8px;
+    padding-right: 2px;
+}
+
+.outline-disclosure li.parent::before {
+    -webkit-user-select: none;
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs.png);
+    -webkit-mask-size: 320px 120px;
+    content: "a";
+    color: transparent;
+    text-shadow: none;
+    position: relative;
+    top: 2px;
+    margin-right: 1px;
+    height: 12px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.outline-disclosure li.parent::before {
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.outline-disclosure li.parent::before {
+    -webkit-mask-position: -4px -96px;
+    background-color: rgb(110, 110, 110);
+}
+
+.outline-disclosure li.parent.expanded::before {
+    -webkit-mask-position: -20px -96px;
+}
+
+.outline-disclosure ol.children {
+    display: none;
+}
+
+.outline-disclosure ol.children.expanded {
+    display: block;
+}
+
+.placard {
+    position: relative;
+    margin-top: 1px;
+    padding: 3px 8px 4px 18px;
+    min-height: 18px;
+    white-space: nowrap;
+}
+
+.placard:nth-of-type(2n) {
+    background-color: rgb(234, 243, 255);
+}
+
+.placard.selected {
+    border-top: 1px solid rgb(172, 172, 172);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(182, 182, 182)), to(rgb(162, 162, 162)));
+    -webkit-background-origin: padding;
+    -webkit-background-clip: padding;
+}
+
+:focus .placard.selected {
+    border-top: 1px solid rgb(70, 103, 215);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(92, 147, 213)), to(rgb(56, 121, 217)));
+}
+
+.placard .title {
+    font-weight: normal;
+    word-wrap: break-word;
+    white-space: normal;
+}
+
+.placard.selected .title {
+    color: white;
+    font-weight: bold;
+}
+
+.placard .subtitle {
+    float: right;
+    font-size: 10px;
+    margin-left: 5px;
+    color: rgba(0, 0, 0, 0.7);
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.placard.selected .subtitle {
+    color: rgba(255, 255, 255, 0.7);
+}
+
+.placard .subtitle a {
+    color: inherit;
+}
+
+.section {
+    position: relative;
+    margin-top: 1px;
+}
+
+.events-pane .section {
+    margin: 0;
+}
+.events-pane .section:not(:nth-of-type(1)) {
+    border-top: 1px solid rgb(231, 231, 231);
+}
+
+.section > .header {
+    padding: 0 8px 0 5px;
+    min-height: 18px;
+    white-space: nowrap;
+    -webkit-background-origin: padding;
+    -webkit-background-clip: padding;
+}
+
+.section > .header::before {
+    -webkit-user-select: none;
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    opacity: 0.5;
+    content: "a";
+    color: transparent;
+    text-shadow: none;
+    float: left;
+    width: 8px;
+    margin-right: 4px;
+    margin-top: 2px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.section > .header::before {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.section > .header::before {
+    background-position: -4px -96px;
+}
+
+.section.expanded > .header::before {
+    background-position: -20px -96px;
+}
+
+.section > .header .title, .event-bar .header .title {
+    font-weight: normal;
+    word-wrap: break-word;
+    white-space: normal;
+    line-height: 18px;
+}
+
+.section > .header .title.blank-title {
+    font-style: italic;
+}
+
+.section > .header label, .event-bar .header label {
+    display: none;
+}
+
+.section.expanded .header label, .event-bar.expanded .header label {
+    display: inline;
+}
+
+.section > .header .subtitle, .event-bar .header .subtitle {
+    float: right;
+    margin-left: 5px;
+    max-width: 55%;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.section > .header .subtitle a {
+    color: inherit;
+}
+
+.section .properties, .event-bar .event-properties {
+    display: none;
+}
+
+.section.expanded .properties, .event-bar.expanded .event-properties {
+    display: block;
+}
+
+.event-bar .event-properties {
+    padding-left: 16px;
+}
+
+.section.no-affect .properties li {
+    opacity: 0.5;
+}
+
+.section.no-affect .properties li.editing {
+    opacity: 1.0;
+}
+
+.properties-tree {
+    margin: 0;
+    padding: 0 6px 2px;
+    list-style: none;
+    min-height: 18px;
+}
+
+.properties-tree li {
+    margin-left: 12px;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    -webkit-user-select: text;
+    cursor: default;
+    padding-top: 2px;
+    line-height: 12px;
+}
+
+.properties-tree li.parent {
+    margin-left: 1px;
+}
+
+
+.properties-tree li.parent::before {
+    -webkit-user-select: none;
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    opacity: 0.5;
+    content: "a";
+    width: 8px;
+    float: left;
+    margin-right: 4px;
+    color: transparent;
+    text-shadow: none;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.properties-tree li.parent::before {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.properties-tree li.parent::before {
+    background-position: -4px -96px;
+}
+
+.properties-tree li.parent.expanded::before {
+    background-position: -20px -96px;
+}
+
+.properties-tree li .info {
+    padding-top: 4px;
+    padding-bottom: 3px;
+}
+
+.properties-tree ol {
+    display: none;
+    margin: 0;
+    -webkit-padding-start: 12px;
+    list-style: none;
+}
+
+.properties-tree ol.expanded {
+    display: block;
+}
+
+.editing {
+    -webkit-user-select: text;
+    -webkit-box-shadow: rgba(0, 0, 0, .5) 3px 3px 4px;
+    outline: 1px solid rgb(66%, 66%, 66%) !important;
+    background-color: white;
+    -webkit-user-modify: read-write-plaintext-only;
+    text-overflow: clip !important;
+    padding-left: 2px;
+    margin-left: -2px;
+    padding-right: 2px;
+    margin-right: -2px;
+    margin-bottom: -1px;
+    padding-bottom: 1px;
+    opacity: 1.0 !important;
+}
+
+.editing, .editing * {
+    color: #222 !important;
+    text-decoration: none !important;
+}
+
+.child-editing {
+    color: #222 !important;
+    text-decoration: none !important;
+    overflow: visible !important;
+}
+
+.editing br {
+    display: none;
+}
+
+.section .properties li.editing {
+    margin-left: 10px;
+    text-overflow: clip;
+}
+
+li.editing .swatch, li.editing .enabled-button,  li.editing-sub-part .delete-button {
+    display: none !important;
+}
+
+.properties-tree.watch-expressions {
+    padding-left: 0 !important;
+}
+
+.properties-tree.watch-expressions > li {
+    padding-left: 4px;
+}
+
+.properties-tree.watch-expressions > li > .value {
+    display: inline;
+    position: static;
+}
+
+.properties-tree.watch-expressions > li:not(.parent) {
+    margin-left: 1px;
+    padding-left: 15px;
+}
+
+.properties-tree.watch-expressions > li.hovered {
+    padding-right: 14px;
+}
+
+.watch-expressions > li.editing-sub-part .text-prompt {
+    display: block;
+    width: 100%;
+}
+
+.watch-expressions > li.editing-sub-part .value, .watch-expressions > li.editing-sub-part .separator  {
+    display: none;
+}
+
+.section .properties li.editing-sub-part {
+    padding: 3px 6px 8px 18px;
+    margin: -3px -6px -8px -6px;
+    text-overflow: clip;
+}
+
+.section .properties .delete-button {
+    width: 10px;
+    height: 10px;
+    background-image: url(Images/deleteIcon.png);
+    background-position: 0 0;
+    background-color: transparent;
+    background-repeat: no-repeat;
+    border: 0 none transparent;
+    position: absolute;
+    right: 8px;
+    display: none;
+}
+
+.section .properties li.hovered .delete-button {
+    display: inline;
+}
+
+.section .properties .name, .event-properties .name, .console-formatted-object .name {
+    color: rgb(136, 19, 145);
+}
+
+.section .properties .dimmed {
+    opacity: 0.6;
+}
+
+.section .properties .value.error {
+    color: red;
+}
+
+.section .properties .number, .event-properties .number {
+    color: blue;
+}
+
+.section .properties .keyword, .event-properties .keyword {
+    color: rgb(136, 19, 79);
+}
+
+.section .properties .color, .event-properties .color {
+    color: rgb(118, 15, 21);
+}
+
+.swatch {
+    margin-left: 1px;
+    margin-right: 2px;
+    width: 1em;
+    height: 1em;
+    position: relative;
+    top: 1px;
+    display: inline-block;
+    background-image: url(Images/checker.png);
+    -webkit-user-select: none;
+}
+
+.swatch-inner {
+    width: 100%;
+    height: 100%;
+    display: inline-block;
+    border: 1px solid rgba(128, 128, 128, 0.6);
+}
+
+.swatch-inner:hover {
+    border: 1px solid rgba(64, 64, 64, 0.8);
+}
+
+.sidebar {
+    overflow-x: hidden;
+    background-color: rgb(214, 221, 229);
+}
+
+body.inactive .sidebar {
+    background-color: rgb(232, 232, 232);
+}
+
+.pane-title-button {
+    color: rgb(6, 6, 6);
+    background-color: transparent;
+    border: 1px solid rgb(165, 165, 165);
+    background-color: rgb(237, 237, 237);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223)));
+    -webkit-border-radius: 12px;
+    -webkit-appearance: none;
+}
+
+.pane-title-button:active {
+    background-color: rgb(215, 215, 215);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239)));
+}
+
+button.show-all-nodes {
+    font-size: 13px;
+    margin: 0;
+    padding: 0 20px;
+    height: 20px;
+    color: rgb(6, 6, 6);
+    background-color: transparent;
+    border: 1px solid rgb(165, 165, 165);
+    background-color: rgb(237, 237, 237);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223)));
+    -webkit-border-radius: 12px;
+    -webkit-appearance: none;
+}
+
+body.inactive button.show-all-nodes {
+    color: rgb(130, 130, 130);
+    border-color: rgb(212, 212, 212);
+    background-color: rgb(239, 239, 239);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(250, 250, 250)), to(rgb(235, 235, 235)));
+}
+
+button.show-all-nodes:active {
+    background-color: rgb(215, 215, 215);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239)));
+}
+
+button.enable-toggle-status-bar-item .glyph {
+    -webkit-mask-position: -192px 0;
+}
+
+button.enable-toggle-status-bar-item.toggled-on .glyph {
+    -webkit-mask-position: -96px -24px;
+}
+
+#console-messages.console-filter-top {
+    margin-top: 23px;
+}
+
+.scope-bar {
+    line-height: 19px;
+    padding-right: 10px;
+    overflow: hidden;
+}
+
+.scope-bar li {
+    display: inline-block;
+    margin: 0 2px;
+    padding: 2px 6px;
+    line-height: 12px;
+    background: transparent;
+    text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0;
+    -webkit-border-radius: 8px;
+    vertical-align: middle;
+}
+
+.scope-bar-divider {
+    background-color: rgba(0, 0, 0, 0.4);
+    height: 16px;
+    width: 1px;
+    vertical-align: middle;
+    display: inline-block;
+}
+
+.scope-bar li.selected, .scope-bar li:hover, .scope-bar li:active {
+    color: white;
+    text-shadow: rgba(0, 0, 0, 0.4) 0 1px 0;
+}
+
+.scope-bar li.all {
+    margin: 0 8px;
+}
+
+.scope-bar li:hover {
+    background: rgba(0, 0, 0, 0.2);
+}
+
+.scope-bar li.selected {
+    background: rgba(0, 0, 0, 0.3);
+}
+
+.scope-bar li:active {
+    background: rgba(0, 0, 0, 0.5);
+}
+
+
+.console-warning-level.repeated-message,
+.console-error-level.repeated-message,
+.console-log-level.repeated-message,
+.console-debug-level.repeated-message{
+    display: -webkit-flex;
+}
+
+.console-user-command-result {
+    display: block;
+}
+
+.source-view-frame {
+    width: 100%;
+    height: 100%;
+}
+
+.sidebar-resizer-vertical {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    width: 5px;
+    z-index: 500;
+    cursor: ew-resize;
+}
+
+.sidebar-tree, .sidebar-tree .children {
+    position: relative;
+    padding: 0;
+    margin: 0;
+    list-style: none;
+}
+
+.sidebar-tree-section {
+    position: relative;
+    height: 18px;
+    padding: 1px 10px 6px 10px;
+    white-space: nowrap;
+    margin-top: 1px;
+    color: rgb(92, 110, 129);
+    text-shadow: rgba(255, 255, 255, 0.75) 0 1px 0;
+}
+
+.sidebar-tree-item {
+    position: relative;
+    height: 36px;
+    padding: 0 5px 0 5px;
+    white-space: nowrap;
+    overflow-x: hidden;
+    overflow-y: hidden;
+    margin-top: 1px;
+    line-height: 34px;
+    border-top: 1px solid transparent;
+}
+
+.sidebar-tree .children {
+    display: none;
+}
+
+.sidebar-tree .children.expanded {
+    display: block;
+}
+
+.sidebar-tree-section + .children > .sidebar-tree-item {
+    padding-left: 10px !important;
+}
+
+.sidebar-tree-section + .children.small > .sidebar-tree-item {
+    padding-left: 17px !important;
+}
+
+.sidebar-tree > .children > .sidebar-tree-item {
+    padding-left: 37px;
+}
+
+.sidebar-tree > .children > .children > .sidebar-tree-item {
+    padding-left: 37px;
+}
+
+.sidebar-tree.hide-disclosure-buttons > .children {
+    display: none;
+}
+
+.sidebar-tree > .children.hide-disclosure-buttons > .children {
+    display: none;
+}
+
+.sidebar-tree.some-expandable:not(.hide-disclosure-buttons) > .sidebar-tree-item:not(.parent) .icon {
+    margin-left: 16px;
+}
+
+.sidebar-tree-item .disclosure-button {
+    float: left;
+    width: 10px;
+    height: 10px;
+    border: 0;
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs.png);
+    -webkit-mask-size: 320px 120px;
+    -webkit-apearance: none;
+    background-color: rgba(0, 0, 0, 0.75);
+    position: relative;
+    top: 10px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.sidebar-tree-item .disclosure-button {
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.sidebar-tree.hide-disclosure-buttons .sidebar-tree-item .disclosure-button {
+    display: none;
+}
+
+.sidebar-tree-item .disclosure-button {
+    -webkit-mask-position: -4px -96px;
+}
+
+.sidebar-tree-item.selected .disclosure-button {
+    background-color: white;
+    -webkit-mask-position: -4px -96px;
+}
+
+.sidebar-tree-item.expanded .disclosure-button {
+    -webkit-mask-position: -20px -96px;
+}
+
+.sidebar-tree-item.selected.expanded .disclosure-button {
+    background-color: white;
+    -webkit-mask-position: -20px -96px;
+}
+
+.sidebar-tree-item .icon {
+    float: left;
+    width: 32px;
+    height: 32px;
+    margin-top: 1px;
+    margin-right: 3px;
+}
+
+li .status {
+    float: right;
+    height: 16px;
+    margin-top: 9px;
+    margin-left: 4px;
+    line-height: 1em;
+}
+
+li .status:empty {
+    display: none;
+}
+
+li .status .bubble {
+    display: inline-block;
+    height: 14px;
+    min-width: 16px;
+    margin-top: 1px;
+    background-color: rgb(128, 151, 189);
+    vertical-align: middle;
+    white-space: nowrap;
+    padding: 1px 4px;
+    text-align: center;
+    font-size: 11px;
+    line-height: normal;
+    font-weight: bold;
+    text-shadow: none;
+    color: white;
+    -webkit-border-radius: 7px;
+}
+
+li .status .bubble:empty {
+    display: none;
+}
+
+li.selected .status .bubble {
+    background-color: white !important;
+    color: rgb(132, 154, 190) !important;
+}
+
+:focus li.selected .status .bubble {
+    color: rgb(36, 98, 172) !important;
+}
+
+body.inactive li.selected .status .bubble {
+    color: rgb(159, 159, 159) !important;
+}
+
+.sidebar-tree.small .sidebar-tree-item, .sidebar-tree .children.small .sidebar-tree-item, .sidebar-tree-item.small, .small .resources-graph-side {
+    height: 20px;
+}
+
+.sidebar-tree.small .sidebar-tree-item .icon, .sidebar-tree .children.small .sidebar-tree-item .icon, .sidebar-tree-item.small .icon {
+    width: 16px;
+    height: 16px;
+}
+
+.sidebar-tree.small .sidebar-tree-item .status, .sidebar-tree .children.small .sidebar-tree-item .status, .sidebar-tree-item.small .status {
+    margin-top: 1px;
+}
+
+.sidebar-tree-item.selected {
+    color: white;
+    border-top: 1px solid rgb(145, 160, 192);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(162, 177, 207)), to(rgb(120, 138, 177)));
+    text-shadow: rgba(0, 0, 0, 0.33) 1px 1px 0;
+    -webkit-background-origin: padding;
+    -webkit-background-clip: padding;
+}
+
+:focus .sidebar-tree-item.selected {
+    border-top: 1px solid rgb(68, 128, 200);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(92, 147, 213)), to(rgb(21, 83, 170)));
+}
+
+body.inactive .sidebar-tree-item.selected {
+    border-top: 1px solid rgb(151, 151, 151);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(180, 180, 180)), to(rgb(138, 138, 138)));
+}
+
+.sidebar-tree-item .titles {
+    position: relative;
+    top: 5px;
+    line-height: 12px;
+    padding-bottom: 1px;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+}
+
+.sidebar-tree-item .titles.no-subtitle {
+    top: 10px;
+}
+
+.sidebar-tree.small .sidebar-tree-item .titles, .sidebar-tree .children.small .sidebar-tree-item .titles, .sidebar-tree-item.small .titles {
+    top: 2px;
+    line-height: normal;
+}
+
+.sidebar-tree:not(.small) .sidebar-tree-item:not(.small) .title::after, .sidebar-tree .children:not(.small) .sidebar-tree-item .title::after {
+    content: "\A";
+    white-space: pre;
+}
+
+.sidebar-tree-item .subtitle {
+    font-size: 80%;
+}
+
+.sidebar-tree.small .sidebar-tree-item .subtitle, .sidebar-tree .children.small .sidebar-tree-item .subtitle, .sidebar-tree-item.small .subtitle {
+    display: none;
+}
+
+.sidebar-tree-item.selected .subtitle {
+    color: white;
+}
+
+.bubble.debug, .console-debug-level .bubble {
+    background-color: rgb(0, 0, 255) !important;
+}
+
+.bubble.warning, .console-warning-level .bubble {
+    background-color: rgb(232, 164, 0) !important;
+}
+
+.bubble.error, .console-error-level .bubble {
+    background-color: rgb(216, 35, 35) !important;
+}
+
+.bubble.search-matches {
+    background-image: url(Images/searchSmallWhite.png);
+    background-repeat: no-repeat;
+    background-position: 3px 2px;
+    padding-left: 13px !important;
+}
+
+li.selected .bubble.search-matches {
+    background-image: url(Images/searchSmallBlue.png);
+}
+
+:focus li.selected .bubble.search-matches {
+    background-image: url(Images/searchSmallBrightBlue.png);
+}
+
+body.inactive li.selected .bubble.search-matches {
+    background-image: url(Images/searchSmallGray.png);
+}
+
+.storage-application-cache-status-icon, .storage-application-cache-connectivity-icon {
+    margin: 5px 5px 0;
+    vertical-align: middle;
+}
+
+.status-bar-divider {
+    margin-left: 7px;
+    border-right: 1px solid #CCC;
+}
+
+.storage-application-cache-status, .storage-application-cache-connectivity {
+    position: relative;
+    top: 4px;
+}
+
+.status-bar-items {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 200px;
+    overflow: hidden;
+    border-left: 1px solid rgb(184, 184, 184);
+    margin-left: -1px;
+}
+
+.node-search-status-bar-item .glyph {
+    -webkit-mask-position: -224px -24px;
+}
+
+.delete-storage-status-bar-item .glyph {
+    -webkit-mask-position: -128px 0;
+}
+
+.clear-storage-status-bar-item .glyph {
+    -webkit-mask-position: -64px 0;
+}
+
+.refresh-storage-status-bar-item .glyph {
+    -webkit-mask-position: 0 0;
+}
+
+.webkit-html-js-node, .webkit-html-css-node {
+    white-space: pre;
+}
+
+.source-frame-breakpoint-condition {
+    z-index: 30;
+    padding: 4px;
+    background-color: rgb(203, 226, 255);
+    -webkit-border-radius: 7px;
+    border: 2px solid rgb(169, 172, 203);
+    width: 90%;
+    pointer-events: auto;
+}
+
+.source-frame-breakpoint-message {
+    background-color: transparent;
+    font-weight: normal;
+    font-size: 11px;
+    text-align: left;
+    text-shadow: none;
+    color: rgb(85, 85, 85);
+    cursor: default;
+    margin: 0 0 2px 0;
+}
+
+#source-frame-breakpoint-condition {
+    margin: 0;
+    border: 1px inset rgb(190, 190, 190) !important;
+    width: 100%;
+    box-shadow: none !important;
+    outline: none !important;
+    -webkit-user-modify: read-write;
+}
+
+.source-frame-popover-title {
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    font-weight: bold;
+    padding-left: 18px;
+}
+
+.source-frame-popover-tree {
+    border-top: 1px solid rgb(194, 194, 147);
+    overflow: auto;
+    position: absolute;
+    top: 15px;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+.source-frame-eval-expression {
+    outline: 1px solid rgb(163, 41, 34);
+    background-color: rgb(255, 255, 194);
+}
+
+.workers-list {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+}
+
+.resource-content-unavailable {
+    color: rgb(50%, 50%, 50%);
+    font-style: italic;
+    font-size: 14px;
+    text-align: center;
+    padding: 32px;
+}
+
+.node-link {
+    text-decoration: underline;
+    cursor: pointer;
+}
+
+.cursor-pointer {
+    cursor: pointer;
+}
+
+.cursor-auto {
+    cursor: auto;
+}
+
+.please-wait-msg {
+    position: absolute;
+    left: 0;
+    top: 0;
+    border: 4px black solid;
+    border-radius: 4px;
+    background-color: black;
+    opacity: 0.85;
+    color: white;
+    font-size: 12px;
+    font-weight: bold;
+    z-index: 10000;
+}
+
+.resource-view.json {
+    padding: 5px;
+}
+
+.resource-view.html iframe {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+}
+
+.soft-context-menu-glass-pane {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    z-index: 20000;
+}
+
+.soft-context-menu {
+    position: absolute;
+    border: 1px solid rgba(196, 196, 196, 0.9);
+    border-top: 1px solid rgba(196, 196, 196, 0.5);
+    border-bottom: 1px solid rgba(150, 150, 150, 0.9);
+    padding: 4px 0 4px 0;
+    border-radius: 4px;
+    background-color: white;
+    -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.25);
+}
+
+.soft-context-menu-item {
+    width: 100%;
+    line-height: 13px;
+    font-size: 14px;
+    border-top: 1px solid transparent;
+    border-bottom: 1px solid transparent;
+    padding: 2px 7px 2px 6px;
+    margin: 0 13px 0 0;
+    white-space: nowrap;
+}
+
+.soft-context-menu-separator {
+    height: 10px;
+    margin: 0 1px;
+}
+
+.soft-context-menu-separator > .separator-line {
+    margin: 0;
+    height: 5px;
+    border-bottom: 1px solid rgb(227, 227, 227);
+    pointer-events: none;
+}
+
+.soft-context-menu-item-mouse-over {
+    border-top: 1px solid rgb(56, 121, 217);
+    border-bottom: 1px solid rgb(56, 121, 217);
+    background-color: rgb(56, 121, 217);
+    color: white;
+}
+
+body.platform-mac .soft-context-menu-item-mouse-over {
+    border-top: 1px solid rgb(90, 131, 236);
+    border-bottom: 1px solid rgb(18, 88, 233);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(100, 140, 243)), to(rgb(36, 101, 243)));
+}
+
+.soft-context-menu-item-checkmark {
+    color: rgb(108, 108, 108);
+    pointer-events: none;
+}
+
+.soft-context-menu-item-submenu-arrow {
+    color: #222;
+    float: right;
+    pointer-events: none;
+}
+
+.soft-context-menu-item-mouse-over .soft-context-menu-item-checkmark {
+    color: white;
+}
+
+.search-view {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+.search-drawer-header input[type="search"].search-config-search {
+    font-size: 11px;
+    margin-left: 4px;
+    color: #303030;
+    position: relative;
+    height: 19px;
+}
+
+body.platform-mac .search-drawer-header input[type="search"].search-config-search {
+    top: 1px;
+}
+
+.search-drawer-header label.search-config-label {
+    margin-left: 8px;
+    color: #303030;
+}
+
+.search-drawer-header input[type="checkbox"].search-config-checkbox {
+    vertical-align: bottom;
+}
+
+body:not(.platform-mac) .search-drawer-header input[type="checkbox"].search-config-checkbox {
+    margin-bottom: 5px;
+}
+
+body.platform-mac .search-drawer-header input[type="checkbox"].search-config-checkbox {
+    margin-bottom: 4px;
+}
+
+.drawer-header {
+    font-size: 11px;
+    border-right: 1px solid rgb(197, 197, 197);
+    line-height: 21px;
+    padding-left: 6px;
+    display: inline-block;
+}
+
+.drawer-header .close-button {
+    position: relative;
+    top: 3px;
+    margin: 0 3px;
+}
+
+#bottom-status-bar-container {
+    -webkit-flex: 1 1 0;
+    width: 0;
+    overflow: hidden;
+}
+
+.animate #bottom-status-bar-container > * {
+    -webkit-transition: opacity 100ms linear;
+}
+
+.search-status-bar-item {
+    display: inline-block;
+    cursor: pointer;
+    font-size: 11px;
+    height: 23px;
+}
+
+.search-status-bar-message {
+    margin-left:5px;
+    margin-right:5px;
+    margin-top:5px;
+    float:left;
+}
+
+.progress-bar-stop-button-item {
+    width: 19px;
+    height: 24px;
+    overflow: hidden;
+}
+
+.progress-bar-stop-button .glyph {
+    -webkit-mask-position: -96px -48px;
+    background-color: rgb(216, 0, 0) !important;
+}
+
+.search-results-status-bar-message {
+    margin-right: 10px;
+    cursor: default;
+    font-size: 11px;
+    float: right;
+    margin-top: 5px;
+}
+
+.search-view .search-results {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    overflow-y: auto;
+}
+
+#search-results-pane-file-based li {
+    list-style: none;
+}
+
+#search-results-pane-file-based ol {
+    -webkit-padding-start: 0;
+    margin-top: 0;
+}
+
+#search-results-pane-file-based ol.children {
+    display: none;
+}
+
+#search-results-pane-file-based ol.children.expanded {
+    display: block;
+}
+
+#search-results-pane-file-based li.parent::before {
+    -webkit-user-select: none;
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    opacity: 0.5;
+    width: 8px;
+    content: "a";
+    color: transparent;
+    margin-left: -5px;
+    padding-right: 4px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+#search-results-pane-file-based li.parent::before {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+#search-results-pane-file-based li.parent::before {
+    background-position: -4px -96px;
+}
+
+#search-results-pane-file-based li.parent.expanded::before {
+    background-position: -20px -96px;
+}
+
+#search-results-pane-file-based .search-result {
+    font-size: 11px;
+    padding: 2px 0 2px 10px;
+    word-wrap: normal;
+    white-space: pre;
+    cursor: pointer;
+}
+
+#search-results-pane-file-based .search-result:hover {
+    background-color: rgba(121, 121, 121, 0.1);
+}
+
+#search-results-pane-file-based .search-result .search-result-file-name {
+    font-weight: bold;
+    color: #222;
+}
+
+#search-results-pane-file-based .search-result .search-result-matches-count {
+    margin-left: 5px;
+    color: #222;
+}
+
+#search-results-pane-file-based .show-more-matches {
+    padding: 4px 0;
+    color: #222;
+    cursor: pointer;
+    font-size: 11px;
+    margin-left: 20px;
+}
+
+#search-results-pane-file-based .show-more-matches:hover {
+    text-decoration: underline;
+}
+
+#search-results-pane-file-based .search-match {
+    word-wrap: normal;
+    white-space: pre;
+}
+
+#search-results-pane-file-based .search-match .webkit-line-number.search-match-line-number {
+    margin-right: 5px;
+    border-right: 1px solid #BBB;
+}
+
+#search-results-pane-file-based .search-match:not(:hover) .webkit-line-number.search-match-line-number {
+    background-color: #F0F0F0;
+}
+
+#search-results-pane-file-based .search-match:hover {
+    background-color: rgba(56, 121, 217, 0.1);
+}
+
+#search-results-pane-file-based .search-match .highlighted-match {
+    background-color: #F1EA00;
+}
+
+#search-results-pane-file-based a {
+    text-decoration: none;
+    display: block;
+}
+
+#search-results-pane-file-based .search-match .search-match-content {
+    color: #000;
+}
+
+.record-cpu-profile-status-bar-item .glyph, .record-profile-status-bar-item .glyph {
+    -webkit-mask-position: -288px 0;
+}
+
+.record-cpu-profile-status-bar-item.toggled-on .glyph, .record-profile-status-bar-item.toggled-on .glyph {
+    -webkit-mask-position: -288px -24px;
+    background-color: rgb(216, 0, 0) !important;
+}
+
+.storage-empty-view, .storage-view .storage-table-error {
+    position: absolute;
+    top: 0;
+    bottom: 25%;
+    left: 0;
+    right: 0;
+    font-size: 24px;
+    color: rgb(75%, 75%, 75%);
+    margin-top: auto;
+    margin-bottom: auto;
+    height: 50px;
+    line-height: 26px;
+    text-align: center;
+    font-weight: bold;
+    padding: 10px;
+    white-space: pre-wrap;
+}
+
+/* Generic suggest box style */
+
+.suggest-box.generic-suggest {
+    margin-left: -1px;
+    border-color: rgb(66%, 66%, 66%);
+}
+
+.suggest-box.generic-suggest.above-anchor {
+   border-radius: 5px 5px 5px 0;
+}
+
+.suggest-box.generic-suggest.under-anchor {
+   border-radius: 0 5px 5px 5px;
+}
+
+/* Custom popup scrollers */
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar, .custom-popup-vertical-scroll ::-webkit-scrollbar {
+    width: 11px;
+    height: 11px;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-corner, .custom-popup-vertical-scroll ::-webkit-scrollbar-corner {
+    display: none;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-resizer, .custom-popup-vertical-scroll ::-webkit-resizer {
+    display: none;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-button, .custom-popup-vertical-scroll ::-webkit-scrollbar-button {
+    display: none;
+}
+
+/* Custom Horizontal Scrollbar Styles */
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar:horizontal:corner-present {
+    border-right-width: 0;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-thumb:horizontal {
+    -webkit-border-image: url(Images/thumbHoriz.png) 0 11 0 11;
+    border-color: transparent;
+    border-width: 0 11px;
+    min-width: 20px;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-thumb:horizontal:hover {
+    -webkit-border-image: url(Images/thumbHoverHoriz.png) 0 11 0 11;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-thumb:horizontal:active {
+    -webkit-border-image: url(Images/thumbActiveHoriz.png) 0 11 0 11;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-track-piece:horizontal:start {
+    margin-left: 5px;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-track-piece:horizontal:end {
+    margin-right: 5px;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-track-piece:horizontal:end:corner-present {
+    margin-right: 4px;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-track-piece:horizontal:decrement {
+    -webkit-border-image: url(Images/trackHoriz.png) 0 11 0 11;
+    border-color: transparent;
+    border-width: 0 0 0 11px;
+}
+
+.custom-popup-horizontal-scroll ::-webkit-scrollbar-track-piece:horizontal:increment {
+    -webkit-border-image: url(Images/trackHoriz.png) 0 11 0 11;
+    border-color: transparent;
+    border-width: 0 11px 0 0;
+}
+
+/* Custom Vertical Scrollbar Styles */
+
+.custom-popup-vertical-scroll ::-webkit-scrollbar:vertical:corner-present {
+    border-bottom-width: 0;
+}
+
+.custom-popup-vertical-scroll ::-webkit-scrollbar-thumb:vertical {
+    -webkit-border-image: url(Images/thumbVert.png) 11 0 11 0;
+    border-color: transparent;
+    border-width: 11px 0;
+    min-height: 20px;
+}
+
+.custom-popup-vertical-scroll ::-webkit-scrollbar-thumb:vertical:hover {
+    -webkit-border-image: url(Images/thumbHoverVert.png) 11 0 11 0;
+}
+
+.custom-popup-vertical-scroll ::-webkit-scrollbar-thumb:vertical:active {
+    -webkit-border-image: url(Images/thumbActiveVert.png) 11 0 11 0;
+}
+
+.custom-popup-vertical-scroll ::-webkit-scrollbar-track-piece:vertical:start {
+    margin-top: 5px;
+}
+
+.custom-popup-vertical-scroll ::-webkit-scrollbar-track-piece:vertical:end {
+    margin-bottom: 5px;
+}
+
+.custom-popup-vertical-scroll ::-webkit-scrollbar-track-piece:vertical:end:corner-present {
+    margin-bottom: 4px;
+}
+
+.custom-popup-vertical-scroll ::-webkit-scrollbar-track-piece:vertical:decrement {
+    -webkit-border-image: url(Images/trackVert.png) 11 0 11 0;
+    border-color: transparent;
+    border-width: 11px 0 0 0;
+}
+
+.custom-popup-vertical-scroll ::-webkit-scrollbar-track-piece:vertical:increment {
+    -webkit-border-image: url(Images/trackVert.png) 11 0 11 0;
+    border-color: transparent;
+    border-width: 0 0 11px 0;
+}
+
+.console-context {
+    max-width: 200px;
+}
+
+.inspector-footer {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    font-size: 11px;
+    height: auto;
+}
+
+.progress-bar-container {
+    display: -webkit-flex;
+    margin: 0 8px;
+    -webkit-flex: 1 0;
+}
+
+.progress-bar-container span {
+    padding: 6px;
+}
+
+.progress-bar-container progress {
+    margin-top: 7px;
+    -webkit-flex: 1 0;
+}
+
+body.platform-mac .progress-bar-container progress {
+    margin-top: 6px;
+}
+
+.progress-bar-container button.status-bar-item {
+    border-left: none;
+    margin-top: 1px;
+}
+
+.source-frame-cursor-position {
+    padding-left: 6px;
+    padding-top: 4px;
+    display: inline-block;
+    pointer-events: auto;
+    -webkit-user-select: text;
+    font-size: 11px;
+    cursor: text;
+    line-height: 14px;
+}
+
+.elements-tree-outline li.parent::before {
+    top: 0 !important;
+}
diff --git a/Source/devtools/front_end/inspector.html b/Source/devtools/front_end/inspector.html
new file mode 100644
index 0000000..3b72720
--- /dev/null
+++ b/Source/devtools/front_end/inspector.html
@@ -0,0 +1,212 @@
+<!--
+Copyright (C) 2006, 2007, 2008 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.
+3.  Neither the name of Apple Computer, 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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8">
+    <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval'">
+    <link rel="stylesheet" type="text/css" href="dialog.css">
+    <link rel="stylesheet" type="text/css" href="inspector.css">
+    <link rel="stylesheet" type="text/css" href="inspectorCommon.css">
+    <link rel="stylesheet" type="text/css" href="inspectorSyntaxHighlight.css">
+    <link rel="stylesheet" type="text/css" href="popover.css">
+    <script type="text/javascript" src="utilities.js"></script>
+    <script type="text/javascript" src="jsdifflib.js"></script>
+    <script type="text/javascript" src="DOMExtension.js"></script>
+    <script type="text/javascript" src="treeoutline.js"></script>
+    <script type="text/javascript" src="inspector.js"></script>
+    <script type="text/javascript" src="UIString.js"></script>
+    <script type="text/javascript" src="InspectorBackend.js"></script>
+    <script type="text/javascript" src="InspectorBackendCommands.js"></script>
+    <script type="text/javascript" src="ExtensionRegistryStub.js"></script>
+    <script type="text/javascript" src="InspectorFrontendAPI.js"></script>
+    <script type="text/javascript" src="Object.js"></script>
+    <script type="text/javascript" src="Settings.js"></script>
+    <script type="text/javascript" src="View.js"></script>
+    <script type="text/javascript" src="UIUtils.js"></script>
+    <script type="text/javascript" src="HelpScreen.js"></script>
+    <script type="text/javascript" src="InspectorFrontendHostStub.js"></script>
+    <script type="text/javascript" src="FileManager.js"></script>
+    <script type="text/javascript" src="Checkbox.js"></script>
+    <script type="text/javascript" src="ContextMenu.js"></script>
+    <script type="text/javascript" src="SoftContextMenu.js"></script>
+    <script type="text/javascript" src="KeyboardShortcut.js"></script>
+    <script type="text/javascript" src="SuggestBox.js"></script>
+    <script type="text/javascript" src="TextPrompt.js"></script>
+    <script type="text/javascript" src="Popover.js"></script>
+    <script type="text/javascript" src="Placard.js"></script>
+    <script type="text/javascript" src="TabbedPane.js"></script>
+    <script type="text/javascript" src="ViewportControl.js"></script> 
+    <script type="text/javascript" src="Drawer.js"></script>
+    <script type="text/javascript" src="ConsoleModel.js"></script>
+    <script type="text/javascript" src="ConsoleMessage.js"></script>
+    <script type="text/javascript" src="ConsoleView.js"></script>
+    <script type="text/javascript" src="Panel.js"></script>
+    <script type="text/javascript" src="InspectorView.js"></script>
+    <script type="text/javascript" src="AdvancedSearchController.js"></script>
+    <script type="text/javascript" src="TimelineGrid.js"></script>
+    <script type="text/javascript" src="OverviewGrid.js"></script>
+    <script type="text/javascript" src="ContentProvider.js"></script>
+    <script type="text/javascript" src="Resource.js"></script>
+    <script type="text/javascript" src="NetworkRequest.js"></script>
+    <script type="text/javascript" src="UISourceCode.js"></script>
+    <script type="text/javascript" src="CSSStyleModel.js"></script>
+    <script type="text/javascript" src="NetworkManager.js"></script>
+    <script type="text/javascript" src="NetworkLog.js"></script>
+    <script type="text/javascript" src="ResourceTreeModel.js"></script>
+    <script type="text/javascript" src="ParsedURL.js"></script>
+    <script type="text/javascript" src="ResourceUtils.js"></script>
+    <script type="text/javascript" src="ResourceType.js"></script>
+    <script type="text/javascript" src="TimelineManager.js"></script>
+    <script type="text/javascript" src="UserAgentSupport.js"></script>
+    <script type="text/javascript" src="Database.js"></script>
+    <script type="text/javascript" src="DOMStorage.js"></script>
+    <script type="text/javascript" src="DataGrid.js"></script>
+    <script type="text/javascript" src="ShowMoreDataGridNode.js"></script>
+    <script type="text/javascript" src="CookiesTable.js"></script>
+    <script type="text/javascript" src="CookieItemsView.js"></script>
+    <script type="text/javascript" src="ApplicationCacheModel.js"></script>
+    <script type="text/javascript" src="IndexedDBModel.js"></script>
+    <script type="text/javascript" src="Spectrum.js"></script>
+    <script type="text/javascript" src="SidebarPane.js"></script>
+    <script type="text/javascript" src="ElementsTreeOutline.js"></script>
+    <script type="text/javascript" src="DOMPresentationUtils.js"></script>
+    <script type="text/javascript" src="SidebarTreeElement.js"></script>
+    <script type="text/javascript" src="Section.js"></script>
+    <script type="text/javascript" src="PropertiesSection.js"></script>
+    <script type="text/javascript" src="RemoteObject.js"></script>
+    <script type="text/javascript" src="ObjectPropertiesSection.js"></script>
+    <script type="text/javascript" src="ObjectPopoverHelper.js"></script>
+    <script type="text/javascript" src="NativeBreakpointsSidebarPane.js"></script>
+    <script type="text/javascript" src="DOMBreakpointsSidebarPane.js"></script>
+    <script type="text/javascript" src="Color.js"></script>
+    <script type="text/javascript" src="CSSMetadata.js"></script>
+    <script type="text/javascript" src="StatusBarButton.js"></script>
+    <script type="text/javascript" src="TextEditor.js"></script>
+    <script type="text/javascript" src="DefaultTextEditor.js"></script>
+    <script type="text/javascript" src="SourceFrame.js"></script>
+    <script type="text/javascript" src="ResourceView.js"></script>
+    <script type="text/javascript" src="FontView.js"></script>
+    <script type="text/javascript" src="ImageView.js"></script>
+    <script type="text/javascript" src="SplitView.js"></script>
+    <script type="text/javascript" src="SidebarView.js"></script>
+    <script type="text/javascript" src="ConsolePanel.js"></script>
+    <script type="text/javascript" src="ExtensionAPI.js"></script>
+    <script type="text/javascript" src="ExtensionAuditCategory.js"></script>
+    <script type="text/javascript" src="ExtensionServer.js"></script>
+    <script type="text/javascript" src="ExtensionView.js"></script>
+    <script type="text/javascript" src="ExtensionPanel.js"></script>
+    <script type="text/javascript" src="EmptyView.js"></script>
+    <script type="text/javascript" src="ScriptFormatter.js"></script>
+    <script type="text/javascript" src="DOMSyntaxHighlighter.js"></script>
+    <script type="text/javascript" src="TextEditorModel.js"></script>
+    <script type="text/javascript" src="TextEditorHighlighter.js"></script>
+    <script type="text/javascript" src="TextUtils.js"></script>
+    <script type="text/javascript" src="SourceTokenizer.js"></script>
+    <script type="text/javascript" src="SourceCSSTokenizer.js"></script>
+    <script type="text/javascript" src="SourceHTMLTokenizer.js"></script>
+    <script type="text/javascript" src="SourceJavaScriptTokenizer.js"></script>
+    <script type="text/javascript" src="FileSystemModel.js"></script>
+    <script type="text/javascript" src="FileUtils.js"></script>
+    <script type="text/javascript" src="DebuggerModel.js"></script>
+    <script type="text/javascript" src="SourceMap.js"></script>
+    <script type="text/javascript" src="SourceMapping.js"></script>
+    <script type="text/javascript" src="Script.js"></script>
+    <script type="text/javascript" src="Linkifier.js"></script>
+    <script type="text/javascript" src="DebuggerScriptMapping.js"></script>
+    <script type="text/javascript" src="PresentationConsoleMessageHelper.js"></script>
+    <script type="text/javascript" src="FileSystemProjectDelegate.js"></script>
+    <script type="text/javascript" src="FileSystemMapping.js"></script>
+    <script type="text/javascript" src="IsolatedFileSystem.js"></script>
+    <script type="text/javascript" src="IsolatedFileSystemManager.js"></script>
+    <script type="text/javascript" src="FileMapping.js"></script>
+    <script type="text/javascript" src="Workspace.js"></script>
+    <script type="text/javascript" src="ContentProviderBasedProjectDelegate.js"></script>
+    <script type="text/javascript" src="SimpleWorkspaceProvider.js"></script>
+    <script type="text/javascript" src="BreakpointManager.js"></script>
+    <script type="text/javascript" src="ContentProviders.js"></script>
+    <script type="text/javascript" src="DefaultScriptMapping.js"></script>
+    <script type="text/javascript" src="ResourceScriptMapping.js"></script>
+    <script type="text/javascript" src="CompilerScriptMapping.js"></script>
+    <script type="text/javascript" src="LiveEditSupport.js"></script>
+    <script type="text/javascript" src="SASSSourceMapping.js"></script>
+    <script type="text/javascript" src="DOMAgent.js"></script>
+    <script type="text/javascript" src="TestController.js"></script>
+    <script type="text/javascript" src="Dialog.js"></script>
+    <script type="text/javascript" src="GoToLineDialog.js"></script>
+    <script type="text/javascript" src="SidebarOverlay.js"></script>
+    <script type="text/javascript" src="SettingsScreen.js"></script>
+    <script type="text/javascript" src="ShortcutsScreen.js"></script>
+    <script type="text/javascript" src="OverridesView.js"></script>
+    <script type="text/javascript" src="HAREntry.js"></script>
+    <script type="text/javascript" src="CookieParser.js"></script>
+    <script type="text/javascript" src="Toolbar.js"></script>
+    <script type="text/javascript" src="SearchController.js"></script>
+    <script type="text/javascript" src="InspectElementModeController.js"></script>
+    <script type="text/javascript" src="WorkerManager.js"></script>
+    <script type="text/javascript" src="UserMetrics.js"></script>
+    <script type="text/javascript" src="RuntimeModel.js"></script>
+    <script type="text/javascript" src="HandlerRegistry.js"></script>
+    <script type="text/javascript" src="SnippetStorage.js"></script>
+    <script type="text/javascript" src="ScriptSnippetModel.js"></script>
+    <script type="text/javascript" src="Progress.js"></script>
+    <script type="text/javascript" src="ProgressIndicator.js"></script>
+    <script type="text/javascript" src="StylesSourceMapping.js"></script>
+    <script type="text/javascript" src="NetworkUISourceCodeProvider.js"></script>
+    <script type="text/javascript" src="ElementsPanelDescriptor.js"></script>
+    <script type="text/javascript" src="NetworkPanelDescriptor.js"></script>
+    <script type="text/javascript" src="ProfilesPanelDescriptor.js"></script>
+    <script type="text/javascript" src="ScriptsPanelDescriptor.js"></script>
+    <script type="text/javascript" src="TimelinePanelDescriptor.js"></script>
+    <script type="text/javascript" src="DockController.js"></script>
+    <script type="text/javascript" src="DevToolsExtensionAPI.js"></script>
+    <script type="text/javascript" src="Tests.js"></script>
+</head>
+<body class="undocked" id="-webkit-web-inspector">
+    <div id="toolbar">
+        <div class="toolbar-item close-left"><div class="close-button" id="close-button-left"></div></div>
+        <div id="toolbar-controls">
+            <div class="toolbar-item"><div id="toolbar-panels-menu" class="hidden">&hellip;</div></div>
+            <div class="toolbar-item"><div id="toolbar-dropdown-arrow" class="hidden">&raquo;</div></div>
+            <div class="toolbar-item close-right"><div class="close-button" id="close-button-right"></div></div>
+        </div>
+    </div>
+    <div id="main">
+        <div id="floating-status-bar-container" class="status-bar"><div id="floating-status-bar-resizer"></div></div>
+    </div>
+    <div id="drawer"></div>
+    <div id="main-status-bar" class="status-bar">
+        <div id="bottom-status-bar-container">
+            <div id="panel-status-bar">
+                <div id="drawer-view-anchor"></div>
+            </div>
+        </div>
+        <div id="error-warning-count" class="hidden"></div>
+    </div>
+</body>
+</html>
diff --git a/Source/devtools/front_end/inspector.js b/Source/devtools/front_end/inspector.js
new file mode 100644
index 0000000..dc3db8b
--- /dev/null
+++ b/Source/devtools/front_end/inspector.js
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
+ * 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 Computer, 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.
+ */
+
+var WebInspector = {
+    _panelDescriptors: function()
+    {
+        this.panels = {};
+        WebInspector.inspectorView = new WebInspector.InspectorView();
+        var parentElement = document.getElementById("main");
+        WebInspector.inspectorView.show(parentElement);
+        WebInspector.inspectorView.addEventListener(WebInspector.InspectorView.Events.PanelSelected, this._panelSelected, this);
+
+        var elements = new WebInspector.ElementsPanelDescriptor();
+        var resources = new WebInspector.PanelDescriptor("resources", WebInspector.UIString("Resources"), "ResourcesPanel", "ResourcesPanel.js");
+        var network = new WebInspector.NetworkPanelDescriptor();
+        var scripts = new WebInspector.ScriptsPanelDescriptor();
+        var timeline = new WebInspector.TimelinePanelDescriptor();
+        var profiles = new WebInspector.ProfilesPanelDescriptor();
+        var audits = new WebInspector.PanelDescriptor("audits", WebInspector.UIString("Audits"), "AuditsPanel", "AuditsPanel.js");
+        var console = new WebInspector.PanelDescriptor("console", WebInspector.UIString("Console"), "ConsolePanel");
+        var allDescriptors = [elements, resources, network, scripts, timeline, profiles, audits, console];
+        var allProfilers = [profiles];
+        if (WebInspector.experimentsSettings.customizableToolbar.isEnabled()) {
+            allProfilers = [];
+            allProfilers.push(new WebInspector.PanelDescriptor("cpu-profiler", WebInspector.UIString("CPU Profiler"), "CPUProfilerPanel", "ProfilesPanel.js"));
+            if (!WebInspector.WorkerManager.isWorkerFrontend())
+                allProfilers.push(new WebInspector.PanelDescriptor("css-profiler", WebInspector.UIString("CSS Profiler"), "CSSSelectorProfilerPanel", "ProfilesPanel.js"));
+            allProfilers.push(new WebInspector.PanelDescriptor("heap-profiler", WebInspector.UIString("Heap Profiler"), "HeapProfilerPanel", "ProfilesPanel.js"));
+            if (!WebInspector.WorkerManager.isWorkerFrontend() && WebInspector.experimentsSettings.canvasInspection.isEnabled())
+                allProfilers.push(new WebInspector.PanelDescriptor("canvas-profiler", WebInspector.UIString("Canvas Profiler"), "CanvasProfilerPanel", "ProfilesPanel.js"));
+            if (!WebInspector.WorkerManager.isWorkerFrontend() && WebInspector.experimentsSettings.nativeMemorySnapshots.isEnabled()) {
+                allProfilers.push(new WebInspector.PanelDescriptor("memory-chart-profiler", WebInspector.UIString("Memory Distribution"), "MemoryChartProfilerPanel", "ProfilesPanel.js"));
+                allProfilers.push(new WebInspector.PanelDescriptor("memory-snapshot-profiler", WebInspector.UIString("Memory Snapshots"), "NativeMemoryProfilerPanel", "ProfilesPanel.js"));
+            }
+            Array.prototype.splice.bind(allDescriptors, allDescriptors.indexOf(profiles), 1).apply(null, allProfilers);
+        }
+
+        var panelDescriptors = [];
+        if (WebInspector.WorkerManager.isWorkerFrontend()) {
+            panelDescriptors.push(scripts);
+            panelDescriptors.push(timeline);
+            panelDescriptors = panelDescriptors.concat(allProfilers);
+            panelDescriptors.push(console);
+            return panelDescriptors;
+        }
+        for (var i = 0; i < allDescriptors.length; ++i)
+            panelDescriptors.push(allDescriptors[i]);
+        return panelDescriptors;
+    },
+
+    _panelSelected: function()
+    {
+        this._toggleConsoleButton.setEnabled(WebInspector.inspectorView.currentPanel().name !== "console");
+    },
+
+    _createGlobalStatusBarItems: function()
+    {
+        var bottomStatusBarContainer = document.getElementById("bottom-status-bar-container");
+
+        // Create main dock button and options.
+        var mainStatusBar = document.getElementById("main-status-bar");
+        mainStatusBar.insertBefore(this.dockController.element, bottomStatusBarContainer);
+
+        this._toggleConsoleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Show console."), "console-status-bar-item");
+        this._toggleConsoleButton.addEventListener("click", this._toggleConsoleButtonClicked.bind(this), false);
+        mainStatusBar.insertBefore(this._toggleConsoleButton.element, bottomStatusBarContainer);
+
+        if (this.inspectElementModeController)
+            mainStatusBar.insertBefore(this.inspectElementModeController.toggleSearchButton.element, bottomStatusBarContainer);
+
+        mainStatusBar.appendChild(this.settingsController.statusBarItem);
+    },
+
+    _toggleConsoleButtonClicked: function()
+    {
+        if (!this._toggleConsoleButton.enabled())
+            return;
+
+        var animationType = window.event && window.event.shiftKey ? WebInspector.Drawer.AnimationType.Slow : WebInspector.Drawer.AnimationType.Normal;
+
+        if (this._toggleConsoleButton.toggled)
+            this.closeConsole(animationType);
+        else
+            this.showConsole(animationType);
+    },
+
+    /**
+     * @param {Element} statusBarElement
+     * @param {WebInspector.View} view
+     * @param {function()=} onclose
+     */
+    showViewInDrawer: function(statusBarElement, view, onclose)
+    {
+        this._toggleConsoleButton.title = WebInspector.UIString("Show console.");
+        this._toggleConsoleButton.toggled = false;
+        this._removeDrawerView();
+
+        var drawerStatusBarHeader = document.createElement("div");
+        drawerStatusBarHeader.className = "drawer-header status-bar-item";
+        drawerStatusBarHeader.appendChild(statusBarElement);
+        drawerStatusBarHeader.onclose = onclose;
+
+        var closeButton = drawerStatusBarHeader.createChild("div", "close-button");
+        closeButton.addEventListener("click", this.closeViewInDrawer.bind(this), false);
+
+        var panelStatusBar = document.getElementById("panel-status-bar");
+        var drawerViewAnchor = document.getElementById("drawer-view-anchor");
+        panelStatusBar.insertBefore(drawerStatusBarHeader, drawerViewAnchor);
+        this._drawerStatusBarHeader = drawerStatusBarHeader;
+        this.drawer.show(view, WebInspector.Drawer.AnimationType.Immediately);
+    },
+
+    closeViewInDrawer: function()
+    {
+        if (this._drawerStatusBarHeader) {
+            this._removeDrawerView();
+
+            // Once drawer is closed console should be shown if it was shown before current view replaced it in drawer. 
+            if (this._consoleWasShown)
+                this.showConsole();
+            else
+                this.drawer.hide(WebInspector.Drawer.AnimationType.Immediately);
+        }
+    },
+
+    _removeDrawerView: function()
+    {
+        if (this._drawerStatusBarHeader) {
+            this._drawerStatusBarHeader.removeSelf();
+            if (this._drawerStatusBarHeader.onclose)
+                this._drawerStatusBarHeader.onclose();
+            delete this._drawerStatusBarHeader;
+        }
+    },
+
+    /**
+     * @param {WebInspector.Drawer.AnimationType=} animationType
+     */
+    showConsole: function(animationType)
+    {
+        animationType = animationType || WebInspector.Drawer.AnimationType.Normal;
+
+        if (this.consoleView.isShowing())
+            return;
+
+        if (WebInspector.drawer.visible)
+            this._removeDrawerView();
+
+        this._toggleConsoleButton.toggled = true;
+        this._toggleConsoleButton.title = WebInspector.UIString("Hide console.");
+        this.drawer.show(this.consoleView, animationType);
+        this._consoleWasShown = true;
+    },
+
+    /**
+     * @param {WebInspector.Drawer.AnimationType=} animationType
+     */
+    closeConsole: function(animationType)
+    {
+        animationType = animationType || WebInspector.Drawer.AnimationType.Normal;
+
+        if (!this.consoleView.isShowing() || !WebInspector.drawer.visible)
+            return;
+
+        this._toggleConsoleButton.toggled = false;
+        this._toggleConsoleButton.title = WebInspector.UIString("Show console.");
+        this.drawer.hide(animationType);
+        this._consoleWasShown = false;
+    },
+
+
+    _updateErrorAndWarningCounts: function()
+    {
+        var errorWarningElement = document.getElementById("error-warning-count");
+        if (!errorWarningElement)
+            return;
+
+        var errors = WebInspector.console.errors;
+        var warnings = WebInspector.console.warnings;
+        if (!errors && !warnings) {
+            errorWarningElement.addStyleClass("hidden");
+            return;
+        }
+
+        errorWarningElement.removeStyleClass("hidden");
+
+        errorWarningElement.removeChildren();
+
+        if (errors) {
+            var errorImageElement = errorWarningElement.createChild("div", "error-icon-small");
+            var errorElement = errorWarningElement.createChild("span");
+            errorElement.id = "error-count";
+            errorElement.textContent = errors;
+        }
+
+        if (warnings) {
+            var warningsImageElement = errorWarningElement.createChild("div", "warning-icon-small");
+            var warningsElement = errorWarningElement.createChild("span");
+            warningsElement.id = "warning-count";
+            warningsElement.textContent = warnings;
+        }
+
+        if (errors) {
+            if (warnings) {
+                if (errors == 1) {
+                    if (warnings == 1)
+                        errorWarningElement.title = WebInspector.UIString("%d error, %d warning", errors, warnings);
+                    else
+                        errorWarningElement.title = WebInspector.UIString("%d error, %d warnings", errors, warnings);
+                } else if (warnings == 1)
+                    errorWarningElement.title = WebInspector.UIString("%d errors, %d warning", errors, warnings);
+                else
+                    errorWarningElement.title = WebInspector.UIString("%d errors, %d warnings", errors, warnings);
+            } else if (errors == 1)
+                errorWarningElement.title = WebInspector.UIString("%d error", errors);
+            else
+                errorWarningElement.title = WebInspector.UIString("%d errors", errors);
+        } else if (warnings == 1)
+            errorWarningElement.title = WebInspector.UIString("%d warning", warnings);
+        else if (warnings)
+            errorWarningElement.title = WebInspector.UIString("%d warnings", warnings);
+        else
+            errorWarningElement.title = null;
+    },
+
+    get inspectedPageDomain()
+    {
+        var parsedURL = WebInspector.inspectedPageURL && WebInspector.inspectedPageURL.asParsedURL();
+        return parsedURL ? parsedURL.host : "";
+    },
+
+    _initializeCapability: function(name, callback, error, result)
+    {
+        Capabilities[name] = result;
+        if (callback)
+            callback();
+    },
+
+    _zoomIn: function()
+    {
+        this._zoomLevel = Math.min(this._zoomLevel + 1, WebInspector.Zoom.Table.length - WebInspector.Zoom.DefaultOffset - 1);
+        this._requestZoom();
+    },
+
+    _zoomOut: function()
+    {
+        this._zoomLevel = Math.max(this._zoomLevel - 1, -WebInspector.Zoom.DefaultOffset);
+        this._requestZoom();
+    },
+
+    _resetZoom: function()
+    {
+        this._zoomLevel = 0;
+        this._requestZoom();
+    },
+
+    _requestZoom: function()
+    {
+        WebInspector.settings.zoomLevel.set(this._zoomLevel);
+        // For backwards compatibility, zoomLevel takes integers (with 0 being default zoom).
+        var index = this._zoomLevel + WebInspector.Zoom.DefaultOffset;
+        index = Math.min(WebInspector.Zoom.Table.length - 1, index);
+        index = Math.max(0, index);
+        InspectorFrontendHost.setZoomFactor(WebInspector.Zoom.Table[index]);
+    },
+
+    _debuggerPaused: function()
+    {
+        // Create scripts panel upon demand.
+        WebInspector.panel("scripts");
+    },
+
+    _setupTethering: function()
+    {
+        if (!this._portForwardings) {
+            this._portForwardings = {};
+            WebInspector.settings.portForwardings.addChangeListener(this._setupTethering.bind(this));
+        }
+        var entries = WebInspector.settings.portForwardings.get();
+        var newForwardings = {};
+        for (var i = 0; i < entries.length; ++i)
+            newForwardings[entries[i].port] = entries[i].location;
+
+        for (var port in this._portForwardings) {
+            if (!newForwardings[port])
+                unbind(port);
+        }
+
+        for (var port in newForwardings) {
+            if (this._portForwardings[port] && newForwardings[port] === this._portForwardings[port])
+                continue;
+            if (this._portForwardings[port])
+              unbind(port);
+            bind(port, newForwardings[port]);
+        }
+        this._portForwardings = newForwardings;
+
+        /**
+         * @param {string} port
+         * @param {string} location
+         */
+        function bind(port, location)
+        {
+            var command = { method: "Tethering.bind", params: { port: parseInt(port, 10), location: location }, id: InspectorBackend.nextCallbackId() };
+            InspectorBackend.sendMessageObjectToBackend(command);
+        }
+
+        /**
+         * @param {string} port
+         */
+        function unbind(port)
+        {
+            var command = { method: "Tethering.unbind", params: { port: parseInt(port, 10) }, id: InspectorBackend.nextCallbackId() };
+            InspectorBackend.sendMessageObjectToBackend(command);
+        }
+    }
+}
+
+WebInspector.Events = {
+    InspectorLoaded: "InspectorLoaded",
+    InspectorClosing: "InspectorClosing"
+}
+
+{(function parseQueryParameters()
+{
+    WebInspector.queryParamsObject = {};
+    var queryParams = window.location.search;
+    if (!queryParams)
+        return;
+    var params = queryParams.substring(1).split("&");
+    for (var i = 0; i < params.length; ++i) {
+        var pair = params[i].split("=");
+        WebInspector.queryParamsObject[pair[0]] = pair[1];
+    }
+})();}
+
+WebInspector.suggestReload = function()
+{
+    if (window.confirm(WebInspector.UIString("It is recommended to restart inspector after making these changes. Would you like to restart it?")))
+        this.reload();
+}
+
+WebInspector.reload = function()
+{
+    var queryParams = window.location.search;
+    var url = window.location.href;
+    url = url.substring(0, url.length - queryParams.length);
+    var queryParamsObject = {};
+    for (var name in WebInspector.queryParamsObject)
+        queryParamsObject[name] = WebInspector.queryParamsObject[name];
+    if (this.dockController)
+        queryParamsObject["dockSide"] = this.dockController.dockSide();
+    var names = Object.keys(queryParamsObject);
+    for (var i = 0; i < names.length; ++i)
+        url += (i ? "&" : "?") + names[i] + "=" + queryParamsObject[names[i]];
+
+    InspectorBackend.disconnect();
+    document.location = url;
+}
+
+WebInspector.loaded = function()
+{
+    InspectorBackend.loadFromJSONIfNeeded("../protocol.json");
+    WebInspector.dockController = new WebInspector.DockController();
+
+    if (WebInspector.WorkerManager.isDedicatedWorkerFrontend()) {
+        // Do not create socket for the worker front-end.
+        WebInspector.doLoadedDone();
+        return;
+    }
+
+    var ws;
+    if ("ws" in WebInspector.queryParamsObject)
+        ws = "ws://" + WebInspector.queryParamsObject.ws;
+    else if ("page" in WebInspector.queryParamsObject) {
+        var page = WebInspector.queryParamsObject.page;
+        var host = "host" in WebInspector.queryParamsObject ? WebInspector.queryParamsObject.host : window.location.host;
+        ws = "ws://" + host + "/devtools/page/" + page;
+    }
+
+    if (ws) {
+        WebInspector.socket = new WebSocket(ws);
+        WebInspector.socket.onmessage = function(message) { InspectorBackend.dispatch(message.data); }
+        WebInspector.socket.onerror = function(error) { console.error(error); }
+        WebInspector.socket.onopen = function() {
+            InspectorFrontendHost.sendMessageToBackend = WebInspector.socket.send.bind(WebInspector.socket);
+            WebInspector.doLoadedDone();
+        }
+        WebInspector.socket.onclose = function() {
+            if (!WebInspector.socket._detachReason)
+                (new WebInspector.RemoteDebuggingTerminatedScreen("websocket_closed")).showModal();
+        }
+        return;
+    }
+
+    WebInspector.doLoadedDone();
+
+    // In case of loading as a web page with no bindings / harness, kick off initialization manually.
+    if (InspectorFrontendHost.isStub) {
+        InspectorFrontendAPI.dispatchQueryParameters();
+        WebInspector._doLoadedDoneWithCapabilities();
+    }
+}
+
+WebInspector.doLoadedDone = function()
+{
+    // Install styles and themes
+    WebInspector.installPortStyles();
+    if (WebInspector.socket)
+        document.body.addStyleClass("remote");
+
+    if (WebInspector.queryParamsObject.toolbarColor && WebInspector.queryParamsObject.textColor)
+        WebInspector.setToolbarColors(WebInspector.queryParamsObject.toolbarColor, WebInspector.queryParamsObject.textColor);
+
+    WebInspector.WorkerManager.loaded();
+
+    PageAgent.canShowFPSCounter(WebInspector._initializeCapability.bind(WebInspector, "canShowFPSCounter", null));
+    PageAgent.canContinuouslyPaint(WebInspector._initializeCapability.bind(WebInspector, "canContinuouslyPaint", null));
+    WorkerAgent.canInspectWorkers(WebInspector._initializeCapability.bind(WebInspector, "canInspectWorkers", WebInspector._doLoadedDoneWithCapabilities.bind(WebInspector)));
+}
+
+WebInspector._doLoadedDoneWithCapabilities = function()
+{
+    new WebInspector.VersionController().updateVersion();
+
+    WebInspector.shortcutsScreen = new WebInspector.ShortcutsScreen();
+    this._registerShortcuts();
+
+    // set order of some sections explicitly
+    WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
+    WebInspector.shortcutsScreen.section(WebInspector.UIString("Elements Panel"));
+
+    var panelDescriptors = this._panelDescriptors();
+    for (var i = 0; i < panelDescriptors.length; ++i)
+        panelDescriptors[i].registerShortcuts();
+
+    this.console = new WebInspector.ConsoleModel();
+    this.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._updateErrorAndWarningCounts, this);
+    this.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._updateErrorAndWarningCounts, this);
+    this.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._updateErrorAndWarningCounts, this);
+
+    WebInspector.CSSMetadata.requestCSSShorthandData();
+
+    this.drawer = new WebInspector.Drawer();
+
+    this.networkManager = new WebInspector.NetworkManager();
+    this.resourceTreeModel = new WebInspector.ResourceTreeModel(this.networkManager);
+    this.debuggerModel = new WebInspector.DebuggerModel();
+    this.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
+    this.networkLog = new WebInspector.NetworkLog();
+    this.domAgent = new WebInspector.DOMAgent();
+    this.domAgent.addEventListener(WebInspector.DOMAgent.Events.InspectNodeRequested, this._inspectNodeRequested, this);
+    this.runtimeModel = new WebInspector.RuntimeModel(this.resourceTreeModel);
+
+    this.consoleView = new WebInspector.ConsoleView(WebInspector.WorkerManager.isWorkerFrontend());
+
+    InspectorBackend.registerInspectorDispatcher(this);
+
+    this.isolatedFileSystemManager = new WebInspector.IsolatedFileSystemManager();
+    this.isolatedFileSystemDispatcher = new WebInspector.IsolatedFileSystemDispatcher(this.isolatedFileSystemManager);
+    this.fileMapping = new WebInspector.FileMapping();
+    this.workspace = new WebInspector.Workspace(this.fileMapping, this.isolatedFileSystemManager.mapping());
+
+    this.cssModel = new WebInspector.CSSStyleModel(this.workspace);
+    this.timelineManager = new WebInspector.TimelineManager();
+    this.userAgentSupport = new WebInspector.UserAgentSupport();
+
+    this.searchController = new WebInspector.SearchController();
+    this.advancedSearchController = new WebInspector.AdvancedSearchController();
+    if (!WebInspector.WorkerManager.isWorkerFrontend())
+        this.inspectElementModeController = new WebInspector.InspectElementModeController();
+
+    this.settingsController = new WebInspector.SettingsController();
+
+    this.domBreakpointsSidebarPane = new WebInspector.DOMBreakpointsSidebarPane();
+
+    this._zoomLevel = WebInspector.settings.zoomLevel.get();
+    if (this._zoomLevel)
+        this._requestZoom();
+
+    var autoselectPanel = WebInspector.UIString("a panel chosen automatically");
+    var openAnchorLocationSetting = WebInspector.settings.createSetting("openLinkHandler", autoselectPanel);
+    this.openAnchorLocationRegistry = new WebInspector.HandlerRegistry(openAnchorLocationSetting);
+    this.openAnchorLocationRegistry.registerHandler(autoselectPanel, function() { return false; });
+
+    this.workspaceController = new WebInspector.WorkspaceController(this.workspace);
+
+    this.fileSystemWorkspaceProvider = new WebInspector.FileSystemWorkspaceProvider(this.isolatedFileSystemManager, this.workspace);
+
+    this.networkWorkspaceProvider = new WebInspector.SimpleWorkspaceProvider(this.workspace, WebInspector.projectTypes.Network);
+    new WebInspector.NetworkUISourceCodeProvider(this.networkWorkspaceProvider, this.workspace);
+
+    this.breakpointManager = new WebInspector.BreakpointManager(WebInspector.settings.breakpoints, this.debuggerModel, this.workspace);
+
+    this.scriptSnippetModel = new WebInspector.ScriptSnippetModel(this.workspace);
+
+    new WebInspector.DebuggerScriptMapping(this.workspace, this.networkWorkspaceProvider);
+    this.liveEditSupport = new WebInspector.LiveEditSupport(this.workspace);
+    this.styleContentBinding = new WebInspector.StyleContentBinding(this.cssModel, this.workspace);
+    new WebInspector.StylesSourceMapping(this.cssModel, this.workspace);
+    if (WebInspector.experimentsSettings.sass.isEnabled())
+        new WebInspector.SASSSourceMapping(this.cssModel, this.workspace, this.networkWorkspaceProvider);
+
+    new WebInspector.PresentationConsoleMessageHelper(this.workspace);
+
+    this._createGlobalStatusBarItems();
+
+    this.toolbar = new WebInspector.Toolbar();
+    WebInspector.startBatchUpdate();
+    for (var i = 0; i < panelDescriptors.length; ++i)
+        WebInspector.inspectorView.addPanel(panelDescriptors[i]);
+    WebInspector.endBatchUpdate();
+
+    this.addMainEventListeners(document);
+
+    window.addEventListener("resize", this.windowResize.bind(this), true);
+
+    var errorWarningCount = document.getElementById("error-warning-count");
+    errorWarningCount.addEventListener("click", this.showConsole.bind(this), false);
+    this._updateErrorAndWarningCounts();
+
+    this.extensionServer.initExtensions();
+
+    this.console.enableAgent();
+
+    function showInitialPanel()
+    {
+        if (!WebInspector.inspectorView.currentPanel())
+            WebInspector.showPanel(WebInspector.settings.lastActivePanel.get());
+    }
+
+    InspectorAgent.enable(showInitialPanel);
+    this.databaseModel = new WebInspector.DatabaseModel();
+    this.domStorageModel = new WebInspector.DOMStorageModel();
+
+    ProfilerAgent.enable();
+
+    if (WebInspector.settings.showPaintRects.get())
+        PageAgent.setShowPaintRects(true);
+
+    if (WebInspector.settings.showDebugBorders.get())
+        PageAgent.setShowDebugBorders(true);
+
+    if (WebInspector.settings.continuousPainting.get())
+        PageAgent.setContinuousPaintingEnabled(true);
+
+    if (WebInspector.settings.showFPSCounter.get())
+        PageAgent.setShowFPSCounter(true);
+
+    this.domAgent._emulateTouchEventsChanged();
+
+    WebInspector.WorkerManager.loadCompleted();
+    InspectorFrontendAPI.loadCompleted();
+
+    if (WebInspector.experimentsSettings.tethering.isEnabled())
+        this._setupTethering();
+
+    WebInspector.notifications.dispatchEventToListeners(WebInspector.Events.InspectorLoaded);
+}
+
+var windowLoaded = function()
+{
+    WebInspector.loaded();
+    window.removeEventListener("DOMContentLoaded", windowLoaded, false);
+    delete windowLoaded;
+};
+
+window.addEventListener("DOMContentLoaded", windowLoaded, false);
+
+// We'd like to enforce asynchronous interaction between the inspector controller and the frontend.
+// It is needed to prevent re-entering the backend code.
+// Also, native dispatches do not guarantee setTimeouts to be serialized, so we
+// enforce serialization using 'messagesToDispatch' queue. It is also important that JSC debugger
+// tests require that each command was dispatch within individual timeout callback, so we don't batch them.
+
+var messagesToDispatch = [];
+
+WebInspector.dispatchQueueIsEmpty = function() {
+    return messagesToDispatch.length == 0;
+}
+
+WebInspector.dispatch = function(message) {
+    messagesToDispatch.push(message);
+    setTimeout(function() {
+        InspectorBackend.dispatch(messagesToDispatch.shift());
+    }, 0);
+}
+
+WebInspector.windowResize = function(event)
+{
+    if (WebInspector.inspectorView)
+        WebInspector.inspectorView.doResize();
+    if (WebInspector.drawer)
+        WebInspector.drawer.resize();
+    if (WebInspector.toolbar)
+        WebInspector.toolbar.resize();
+    if (WebInspector.settingsController)
+        WebInspector.settingsController.resize();
+}
+
+WebInspector.setDockingUnavailable = function(unavailable)
+{
+    if (this.dockController)
+        this.dockController.setDockingUnavailable(unavailable);
+}
+
+WebInspector.close = function(event)
+{
+    if (this._isClosing)
+        return;
+    this._isClosing = true;
+    this.notifications.dispatchEventToListeners(WebInspector.Events.InspectorClosing);
+    InspectorFrontendHost.closeWindow();
+}
+
+WebInspector.documentClick = function(event)
+{
+    var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
+    if (!anchor || (anchor.target === "_blank"))
+        return;
+
+    // Prevent the link from navigating, since we don't do any navigation by following links normally.
+    event.consume(true);
+
+    function followLink()
+    {
+        if (WebInspector.isBeingEdited(event.target) || WebInspector._showAnchorLocation(anchor))
+            return;
+
+        const profileMatch = WebInspector.ProfilesPanelDescriptor.ProfileURLRegExp.exec(anchor.href);
+        if (profileMatch) {
+            WebInspector.showPanel("profiles").showProfile(profileMatch[1], profileMatch[2]);
+            return;
+        }
+
+        var parsedURL = anchor.href.asParsedURL();
+        if (parsedURL && parsedURL.scheme === "webkit-link-action") {
+            if (parsedURL.host === "show-panel") {
+                var panel = parsedURL.path.substring(1);
+                if (WebInspector.panel(panel))
+                    WebInspector.showPanel(panel);
+            }
+            return;
+        }
+
+        InspectorFrontendHost.openInNewTab(anchor.href);
+    }
+
+    if (WebInspector.followLinkTimeout)
+        clearTimeout(WebInspector.followLinkTimeout);
+
+    if (anchor.preventFollowOnDoubleClick) {
+        // Start a timeout if this is the first click, if the timeout is canceled
+        // before it fires, then a double clicked happened or another link was clicked.
+        if (event.detail === 1)
+            WebInspector.followLinkTimeout = setTimeout(followLink, 333);
+        return;
+    }
+
+    followLink();
+}
+
+WebInspector.openResource = function(resourceURL, inResourcesPanel)
+{
+    var resource = WebInspector.resourceForURL(resourceURL);
+    if (inResourcesPanel && resource)
+        WebInspector.showPanel("resources").showResource(resource);
+    else
+        InspectorFrontendHost.openInNewTab(resourceURL);
+}
+
+WebInspector._registerShortcuts = function()
+{
+    var shortcut = WebInspector.KeyboardShortcut;
+    var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("All Panels"));
+    var keys = [
+        shortcut.makeDescriptor("[", shortcut.Modifiers.CtrlOrMeta),
+        shortcut.makeDescriptor("]", shortcut.Modifiers.CtrlOrMeta)
+    ];
+    section.addRelatedKeys(keys, WebInspector.UIString("Go to the panel to the left/right"));
+
+    keys = [
+        shortcut.makeDescriptor("[", shortcut.Modifiers.CtrlOrMeta | shortcut.Modifiers.Alt),
+        shortcut.makeDescriptor("]", shortcut.Modifiers.CtrlOrMeta | shortcut.Modifiers.Alt)
+    ];
+    section.addRelatedKeys(keys, WebInspector.UIString("Go back/forward in panel history"));
+
+    section.addKey(shortcut.makeDescriptor(shortcut.Keys.Esc), WebInspector.UIString("Toggle console"));
+    section.addKey(shortcut.makeDescriptor("f", shortcut.Modifiers.CtrlOrMeta), WebInspector.UIString("Search"));
+
+    var advancedSearchShortcut = WebInspector.AdvancedSearchController.createShortcut();
+    section.addKey(advancedSearchShortcut, WebInspector.UIString("Search across all sources"));
+
+    var inspectElementModeShortcut = WebInspector.InspectElementModeController.createShortcut();
+    section.addKey(inspectElementModeShortcut, WebInspector.UIString("Select node to inspect"));
+
+    var openResourceShortcut = WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
+    section.addKey(openResourceShortcut, WebInspector.UIString("Go to source"));
+
+    if (WebInspector.isMac()) {
+        keys = [
+            shortcut.makeDescriptor("g", shortcut.Modifiers.Meta),
+            shortcut.makeDescriptor("g", shortcut.Modifiers.Meta | shortcut.Modifiers.Shift)
+        ];
+        section.addRelatedKeys(keys, WebInspector.UIString("Find next/previous"));
+    }
+
+    var goToShortcut = WebInspector.GoToLineDialog.createShortcut();
+    section.addKey(goToShortcut, WebInspector.UIString("Go to line"));
+
+    keys = [
+        shortcut.Keys.F1,
+        shortcut.makeDescriptor("?")
+    ];
+    section.addAlternateKeys(keys, WebInspector.UIString("Show keyboard shortcuts"));
+}
+
+/**
+ * @param {KeyboardEvent} event
+ */
+WebInspector.documentKeyDown = function(event)
+{
+    const helpKey = WebInspector.isMac() ? "U+003F" : "U+00BF"; // "?" for both platforms
+
+    if (event.keyIdentifier === "F1" ||
+        (event.keyIdentifier === helpKey && event.shiftKey && (!WebInspector.isBeingEdited(event.target) || event.metaKey))) {
+        this.settingsController.showSettingsScreen(WebInspector.SettingsScreen.Tabs.General);
+        event.consume(true);
+        return;
+    }
+
+    if (WebInspector.currentFocusElement() && WebInspector.currentFocusElement().handleKeyEvent) {
+        WebInspector.currentFocusElement().handleKeyEvent(event);
+        if (event.handled) {
+            event.consume(true);
+            return;
+        }
+    }
+
+    if (WebInspector.inspectorView.currentPanel()) {
+        WebInspector.inspectorView.currentPanel().handleShortcut(event);
+        if (event.handled) {
+            event.consume(true);
+            return;
+        }
+    }
+
+    if (WebInspector.searchController.handleShortcut(event))
+        return;
+    if (WebInspector.advancedSearchController.handleShortcut(event))
+        return;
+    if (WebInspector.inspectElementModeController && WebInspector.inspectElementModeController.handleShortcut(event))
+        return;
+
+    switch (event.keyIdentifier) {
+        case "U+004F": // O key
+            if (!event.shiftKey && !event.altKey && WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) {
+                WebInspector.showPanel("scripts").showGoToSourceDialog();
+                event.consume(true);
+            }
+            break;
+        case "U+0052": // R key
+            if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) {
+                PageAgent.reload(event.shiftKey);
+                event.consume(true);
+            }
+            if (window.DEBUG && event.altKey) {
+                WebInspector.reload();
+                return;
+            }
+            break;
+        case "F5":
+            if (!WebInspector.isMac()) {
+                PageAgent.reload(event.ctrlKey || event.shiftKey);
+                event.consume(true);
+            }
+            break;
+    }
+
+    var isValidZoomShortcut = WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) &&
+        !event.altKey &&
+        !InspectorFrontendHost.isStub;
+    switch (event.keyCode) {
+        case 107: // +
+        case 187: // +
+            if (isValidZoomShortcut) {
+                WebInspector._zoomIn();
+                event.consume(true);
+            }
+            break;
+        case 109: // -
+        case 189: // -
+            if (isValidZoomShortcut) {
+                WebInspector._zoomOut();
+                event.consume(true);
+            }
+            break;
+        case 48: // 0
+            // Zoom reset shortcut does not allow "Shift" when handled by the browser.
+            if (isValidZoomShortcut && !event.shiftKey) {
+                WebInspector._resetZoom();
+                event.consume(true);
+            }
+            break;
+    }
+}
+
+WebInspector.postDocumentKeyDown = function(event)
+{
+    if (event.handled)
+        return;
+
+    if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
+        // If drawer is open with some view other than console then close it.
+        if (!this._toggleConsoleButton.toggled && WebInspector.drawer.visible)
+            this.closeViewInDrawer();
+        else
+            this._toggleConsoleButtonClicked();
+    }
+}
+
+WebInspector.documentCanCopy = function(event)
+{
+    if (WebInspector.inspectorView.currentPanel() && WebInspector.inspectorView.currentPanel().handleCopyEvent)
+        event.preventDefault();
+}
+
+WebInspector.documentCopy = function(event)
+{
+    if (WebInspector.inspectorView.currentPanel() && WebInspector.inspectorView.currentPanel().handleCopyEvent)
+        WebInspector.inspectorView.currentPanel().handleCopyEvent(event);
+    WebInspector.documentCopyEventFired(event);
+}
+
+WebInspector.documentCopyEventFired = function(event)
+{
+}
+
+WebInspector.contextMenuEventFired = function(event)
+{
+    if (event.handled || event.target.hasStyleClass("popup-glasspane"))
+        event.preventDefault();
+}
+
+WebInspector.showPanel = function(panel)
+{
+    return WebInspector.inspectorView.showPanel(panel);
+}
+
+WebInspector.panel = function(panel)
+{
+    return WebInspector.inspectorView.panel(panel);
+}
+
+WebInspector.bringToFront = function()
+{
+    InspectorFrontendHost.bringToFront();
+}
+
+/**
+ * @param {string=} messageLevel
+ * @param {boolean=} showConsole
+ */
+WebInspector.log = function(message, messageLevel, showConsole)
+{
+    // remember 'this' for setInterval() callback
+    var self = this;
+
+    // return indication if we can actually log a message
+    function isLogAvailable()
+    {
+        return WebInspector.ConsoleMessage && WebInspector.RemoteObject && self.console;
+    }
+
+    // flush the queue of pending messages
+    function flushQueue()
+    {
+        var queued = WebInspector.log.queued;
+        if (!queued)
+            return;
+
+        for (var i = 0; i < queued.length; ++i)
+            logMessage(queued[i]);
+
+        delete WebInspector.log.queued;
+    }
+
+    // flush the queue if it console is available
+    // - this function is run on an interval
+    function flushQueueIfAvailable()
+    {
+        if (!isLogAvailable())
+            return;
+
+        clearInterval(WebInspector.log.interval);
+        delete WebInspector.log.interval;
+
+        flushQueue();
+    }
+
+    // actually log the message
+    function logMessage(message)
+    {
+        // post the message
+        var msg = WebInspector.ConsoleMessage.create(
+            WebInspector.ConsoleMessage.MessageSource.Other,
+            messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug,
+            message);
+
+        self.console.addMessage(msg);
+        if (showConsole)
+            WebInspector.showConsole();
+    }
+
+    // if we can't log the message, queue it
+    if (!isLogAvailable()) {
+        if (!WebInspector.log.queued)
+            WebInspector.log.queued = [];
+
+        WebInspector.log.queued.push(message);
+
+        if (!WebInspector.log.interval)
+            WebInspector.log.interval = setInterval(flushQueueIfAvailable, 1000);
+
+        return;
+    }
+
+    // flush the pending queue if any
+    flushQueue();
+
+    // log the message
+    logMessage(message);
+}
+
+WebInspector.showErrorMessage = function(error)
+{
+    WebInspector.log(error, WebInspector.ConsoleMessage.MessageLevel.Error, true);
+}
+
+// Inspector.inspect protocol event
+WebInspector.inspect = function(payload, hints)
+{
+    var object = WebInspector.RemoteObject.fromPayload(payload);
+    if (object.subtype === "node") {
+        function callback(nodeId)
+        {
+            WebInspector._updateFocusedNode(nodeId);
+            object.release();
+        }
+        object.pushNodeToFrontend(callback);
+        return;
+    }
+
+    if (hints.databaseId)
+        WebInspector.showPanel("resources").selectDatabase(WebInspector.databaseModel.databaseForId(hints.databaseId));
+    else if (hints.domStorageId)
+        WebInspector.showPanel("resources").selectDOMStorage(WebInspector.domStorageModel.storageForId(hints.domStorageId));
+
+    object.release();
+}
+
+// Inspector.detached protocol event
+WebInspector.detached = function(reason)
+{
+    WebInspector.socket._detachReason = reason;
+    (new WebInspector.RemoteDebuggingTerminatedScreen(reason)).showModal();
+}
+
+WebInspector.targetCrashed = function()
+{
+    (new WebInspector.HelpScreenUntilReload(
+        WebInspector.UIString("Inspected target crashed"),
+        WebInspector.UIString("Inspected target has crashed. Once it reloads we will attach to it automatically."))).showModal();
+}
+
+WebInspector._inspectNodeRequested = function(event)
+{
+    WebInspector._updateFocusedNode(event.data);
+}
+
+WebInspector._updateFocusedNode = function(nodeId)
+{
+    if (WebInspector.inspectElementModeController && WebInspector.inspectElementModeController.enabled()) {
+        InspectorFrontendHost.bringToFront();
+        WebInspector.inspectElementModeController.disable();
+    }
+    WebInspector.showPanel("elements").revealAndSelectNode(nodeId);
+}
+
+WebInspector._showAnchorLocation = function(anchor)
+{
+    if (WebInspector.openAnchorLocationRegistry.dispatch({ url: anchor.href, lineNumber: anchor.lineNumber}))
+        return true;
+    var preferredPanel = this.panels[anchor.preferredPanel];
+    if (preferredPanel && WebInspector._showAnchorLocationInPanel(anchor, preferredPanel))
+        return true;
+    if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("scripts")))
+        return true;
+    if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("resources")))
+        return true;
+    if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("network")))
+        return true;
+    return false;
+}
+
+WebInspector._showAnchorLocationInPanel = function(anchor, panel)
+{
+    if (!panel || !panel.canShowAnchorLocation(anchor))
+        return false;
+
+    // FIXME: support webkit-html-external-link links here.
+    if (anchor.hasStyleClass("webkit-html-external-link")) {
+        anchor.removeStyleClass("webkit-html-external-link");
+        anchor.addStyleClass("webkit-html-resource-link");
+    }
+
+    WebInspector.inspectorView.showPanelForAnchorNavigation(panel);
+    panel.showAnchorLocation(anchor);
+    return true;
+}
+
+WebInspector.evaluateInConsole = function(expression, showResultOnly)
+{
+    this.showConsole();
+    this.consoleView.evaluateUsingTextPrompt(expression, showResultOnly);
+}
+
+WebInspector.addMainEventListeners = function(doc)
+{
+    doc.addEventListener("keydown", this.documentKeyDown.bind(this), true);
+    doc.addEventListener("keydown", this.postDocumentKeyDown.bind(this), false);
+    doc.addEventListener("beforecopy", this.documentCanCopy.bind(this), true);
+    doc.addEventListener("copy", this.documentCopy.bind(this), false);
+    doc.addEventListener("contextmenu", this.contextMenuEventFired.bind(this), true);
+    doc.addEventListener("click", this.documentClick.bind(this), true);
+}
+
+WebInspector.Zoom = {
+    Table: [0.25, 0.33, 0.5, 0.66, 0.75, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5],
+    DefaultOffset: 6
+}
+
+
+// Ex-DevTools.js content
+
+/**
+ * @param {ExtensionDescriptor} extensionInfo
+ * @return {string}
+ */
+function buildPlatformExtensionAPI(extensionInfo)
+{
+    return "var extensionInfo = " + JSON.stringify(extensionInfo) + ";" +
+       "var tabId = " + WebInspector._inspectedTabId + ";" +
+       platformExtensionAPI.toString();
+}
+
+WebInspector.setInspectedTabId = function(tabId)
+{
+    WebInspector._inspectedTabId = tabId;
+}
+
+/**
+ * @return {string}
+ */
+WebInspector.getSelectionBackgroundColor = function()
+{
+    return InspectorFrontendHost.getSelectionBackgroundColor();
+}
+
+/**
+ * @return {string}
+ */
+WebInspector.getSelectionForegroundColor = function()
+{
+    return InspectorFrontendHost.getSelectionForegroundColor();
+}
+
+window.DEBUG = true;
diff --git a/Source/devtools/front_end/inspectorCommon.css b/Source/devtools/front_end/inspectorCommon.css
new file mode 100644
index 0000000..300ebe1
--- /dev/null
+++ b/Source/devtools/front_end/inspectorCommon.css
@@ -0,0 +1,244 @@
+html {
+    height: 100%;
+}
+
+body {
+    cursor: default;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    overflow: hidden;
+    font-family: Lucida Grande, sans-serif;
+    font-size: 12px;
+    margin: 0;
+    tab-size: 4;
+    -webkit-user-select: none;
+    color: #222;
+}
+
+body.platform-linux {
+  color: rgb(48, 57, 66);
+  font-family: Ubuntu, Arial, sans-serif;
+}
+
+body.platform-mac {
+  color: rgb(48, 57, 66);
+  font-family: 'Lucida Grande', sans-serif;
+}
+
+body.platform-windows {
+  font-family: 'Segoe UI', Tahoma, sans-serif;
+}
+
+body.dock-to-right:not(.undocked) {
+    border-left: 1px solid rgb(80, 80, 80);
+}
+
+body.dock-to-right.inactive:not(.undocked) {
+    border-left: 1px solid rgb(64%, 64%, 64%);
+}
+
+* {
+    -webkit-box-sizing: border-box;
+}
+
+:focus {
+    outline: none;
+}
+
+img {
+    -webkit-user-drag: none;
+}
+
+iframe, a img {
+    border: none;
+}
+
+iframe.view {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+}
+
+.hidden {
+    display: none !important;
+}
+
+.monospace {
+    font-size: 10px !important;
+    font-family: monospace;
+}
+
+.resources-dividers {
+    position: absolute;
+    left: 0;
+    right: 0;
+    height: 100%;
+    top: 0;
+    z-index: -100;
+}
+
+.resources-event-dividers {
+    position: absolute;
+    left: 0;
+    right: 0;
+    height: 100%;
+    top: 0;
+    z-index: 300;
+    pointer-events: none;
+}
+
+.resources-dividers-label-bar {
+    position: absolute;
+    top: 0;
+    left: 0px;
+    right: 0;
+    background-color: rgba(255, 255, 255, 0.8);
+    background-clip: padding;
+    border-bottom: 1px solid rgba(0, 0, 0, 0.3);
+    height: 20px;
+    z-index: 200;
+    pointer-events: none;
+    cursor: move;
+    overflow: hidden;
+}
+
+.resources-divider {
+    position: absolute;
+    width: 1px;
+    top: 0;
+    bottom: 0;
+    background-color: rgba(0, 0, 0, 0.1);
+}
+
+.resources-event-divider-padding {
+    position: absolute;
+    width: 8px;
+    top: 0;
+    bottom: 0;
+    pointer-events: auto;
+}
+
+.resources-event-divider {
+    position: absolute;
+    width: 2px;
+    top: 0;
+    bottom: 0;
+    z-index: 300;
+}
+
+.resources-divider-label {
+    position: absolute;
+    top: 4px;
+    right: 3px;
+    font-size: 80%;
+    white-space: nowrap;
+    pointer-events: none;
+}
+
+.overview-grid-window-selector {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    background-color: rgba(125, 173, 217, 0.5);
+    z-index: 250;
+}
+
+.overview-grid-window {
+    background-color: white;
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 60px;
+    z-index: 150;
+}
+
+.overview-grid-dividers-background {
+    left: 0%;
+    right: 0%;
+    top: 0px;
+    bottom: 60px;
+    background-color: black;
+    position: absolute;
+}
+
+.overview-grid-window-rulers {
+    top: 0;
+    bottom: 0;
+    position: absolute;
+    opacity: 0.2;
+    border-right: 1px solid black;
+    border-left: 1px solid black;
+    z-index: 150;
+    pointer-events: none;
+}
+
+.overview-grid-window-resizer {
+    position: absolute;
+    top: 0px;
+    bottom: 60px;
+    width: 5px;
+    margin-left: -3px;
+    margin-right: -2px;
+    background-color: rgb(153, 153, 153);
+    z-index: 500;
+    cursor: ew-resize;
+    -webkit-border-radius: 2px;
+    -webkit-box-shadow: white 1px 0 0, white -1px 0 0, white 0 1px 0, white 0 -1px 0;
+}
+
+/* Network timing is shared between popover and network item view pane */
+
+.network-timing-row {
+    position: relative;
+    height: 16px;
+}
+
+.network-timing-bar {
+    position: absolute;
+    background-color: red;
+    border-left: 1px solid red;
+    opacity: 0.4;
+    top: 0;
+    bottom: 0;
+}
+
+.network-timing-bar-title {
+    position: absolute;
+    color: #222;
+    top: 1px;
+}
+
+.webkit-search-result {
+    -webkit-border-radius: 4px;
+    padding: 2px 2px 2px 3px;
+    margin: -2px -2px -2px -3px;
+    opacity: 0.8;
+    -webkit-box-shadow: rgba(0, 0, 0, .5) 3px 3px 4px;
+    background-color: rgb(241, 234, 0);
+    color: #222;
+}
+
+.sidebar-separator {
+    background-color: rgb(230, 230, 230);
+    padding: 0 5px;
+    border-top: 1px solid rgb(189, 189, 189);
+    border-bottom: 1px solid rgb(189, 189, 189);
+    color: rgb(50, 50, 50);
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    line-height: 16px;
+}
+
+.sidebar-label {
+    font-size: 11px;
+}
+
diff --git a/Source/devtools/front_end/inspectorSyntaxHighlight.css b/Source/devtools/front_end/inspectorSyntaxHighlight.css
new file mode 100644
index 0000000..dc51077
--- /dev/null
+++ b/Source/devtools/front_end/inspectorSyntaxHighlight.css
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2009 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+.webkit-css-comment {
+    color: rgb(0, 116, 0);
+}
+
+.webkit-css-url, .webkit-css-color, .webkit-css-string, .webkit-css-keyword {
+    color: rgb(7, 144, 154);
+ }
+
+.webkit-css-number {
+    color: rgb(50, 0, 255);
+}
+
+.webkit-css-property, .webkit-css-at-rule {
+    color: rgb(200, 0, 0);
+}
+
+.webkit-css-selector {
+    color: #222;
+}
+
+.webkit-css-bang-keyword {
+    color: rgb(200, 0, 180);
+}
+
+.webkit-javascript-undef {
+    color: rgb(123, 123, 123);
+}
+
+.webkit-javascript-comment {
+    color: rgb(0, 116, 0);
+}
+
+.webkit-javascript-keyword {
+    color: rgb(170, 13, 145);
+}
+
+.webkit-javascript-number {
+    color: rgb(28, 0, 207);
+}
+
+.webkit-javascript-string, .webkit-javascript-regexp {
+    color: rgb(196, 26, 22);
+}
+
+.webkit-javascript-ident {
+    color: #222;
+}
+
+.webkit-whitespace-1::before {
+    content: "·";
+}
+
+.webkit-whitespace-2::before {
+    content: "··";
+}
+
+.webkit-whitespace-4::before {
+    content: "····";
+}
+
+.webkit-whitespace-8::before {
+    content: "········";
+}
+
+.webkit-whitespace-16::before {
+    content: "················";
+}
+
+.webkit-whitespace::before {
+    position: absolute;
+    pointer-events: none;
+    color: rgb(175, 175, 175);
+}
+
+.webkit-html-comment {
+    /* Keep this in sync with view-source.css (.webkit-html-comment) */
+    color: rgb(35, 110, 37);
+}
+
+.webkit-html-tag {
+    /* Keep this in sync with view-source.css (.webkit-html-tag) */
+    color: rgb(136, 18, 128);
+}
+
+.webkit-html-doctype {
+    /* Keep this in sync with view-source.css (.webkit-html-doctype) */
+    color: rgb(192, 192, 192);
+}
+
+.webkit-html-attribute-name {
+    /* Keep this in sync with view-source.css (.webkit-html-attribute-name) */
+    color: rgb(153, 69, 0);
+}
+
+.webkit-html-attribute-value {
+    /* Keep this in sync with view-source.css (.webkit-html-attribute-value) */
+    color: rgb(26, 26, 166);
+}
+
+.webkit-html-external-link, .webkit-html-resource-link {
+    /* Keep this in sync with view-source.css (.webkit-html-external-link, .webkit-html-resource-link) */
+    color: #00e;
+}
+
+.webkit-html-external-link {
+    /* Keep this in sync with view-source.css (.webkit-html-external-link) */
+    text-decoration: none;
+}
+
+.webkit-html-external-link:hover {
+    /* Keep this in sync with view-source.css (.webkit-html-external-link:hover) */
+    text-decoration: underline;
+}
diff --git a/Source/devtools/front_end/jsdifflib.js b/Source/devtools/front_end/jsdifflib.js
new file mode 100644
index 0000000..f86a8f7
--- /dev/null
+++ b/Source/devtools/front_end/jsdifflib.js
@@ -0,0 +1,409 @@
+/*
+ * This is part of jsdifflib v1.0. <http://snowtide.com/jsdifflib>
+ *
+ * Copyright (c) 2007, Snowtide Informatics Systems, 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 the Snowtide Informatics Systems 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.
+ **/
+/* Author: Chas Emerick <cemerick@snowtide.com> */
+/* taken from https://github.com/cemerick/jsdifflib */
+
+__whitespace = {" ":true, "\t":true, "\n":true, "\f":true, "\r":true};
+
+difflib = {
+    defaultJunkFunction: function (c) {
+        return __whitespace.hasOwnProperty(c);
+    },
+
+    stripLinebreaks: function (str) { return str.replace(/^[\n\r]*|[\n\r]*$/g, ""); },
+
+    stringAsLines: function (str) {
+        var lfpos = str.indexOf("\n");
+        var crpos = str.indexOf("\r");
+        var linebreak = ((lfpos > -1 && crpos > -1) || crpos < 0) ? "\n" : "\r";
+
+        var lines = str.split(linebreak);
+        for (var i = 0; i < lines.length; i++) {
+            lines[i] = difflib.stripLinebreaks(lines[i]);
+        }
+
+        return lines;
+    },
+
+    // iteration-based reduce implementation
+    __reduce: function (func, list, initial) {
+        if (initial != null) {
+            var value = initial;
+            var idx = 0;
+        } else if (list) {
+            var value = list[0];
+            var idx = 1;
+        } else {
+            return null;
+        }
+
+        for (; idx < list.length; idx++) {
+            value = func(value, list[idx]);
+        }
+
+        return value;
+    },
+
+    // comparison function for sorting lists of numeric tuples
+    __ntuplecomp: function (a, b) {
+        var mlen = Math.max(a.length, b.length);
+        for (var i = 0; i < mlen; i++) {
+            if (a[i] < b[i]) return -1;
+            if (a[i] > b[i]) return 1;
+        }
+
+        return a.length == b.length ? 0 : (a.length < b.length ? -1 : 1);
+    },
+
+    __calculate_ratio: function (matches, length) {
+        return length ? 2.0 * matches / length : 1.0;
+    },
+
+    // returns a function that returns true if a key passed to the returned function
+    // is in the dict (js object) provided to this function; replaces being able to
+    // carry around dict.has_key in python...
+    __isindict: function (dict) {
+        return function (key) { return dict.hasOwnProperty(key); };
+    },
+
+    // replacement for python's dict.get function -- need easy default values
+    __dictget: function (dict, key, defaultValue) {
+        return dict.hasOwnProperty(key) ? dict[key] : defaultValue;
+    },  
+
+    SequenceMatcher: function (a, b, isjunk) {
+        this.set_seqs = function (a, b) {
+            this.set_seq1(a);
+            this.set_seq2(b);
+        }
+
+        this.set_seq1 = function (a) {
+            if (a == this.a) return;
+            this.a = a;
+            this.matching_blocks = this.opcodes = null;
+        }
+
+        this.set_seq2 = function (b) {
+            if (b == this.b) return;
+            this.b = b;
+            this.matching_blocks = this.opcodes = this.fullbcount = null;
+            this.__chain_b();
+        }
+
+        this.__chain_b = function () {
+            var b = this.b;
+            var n = b.length;
+            var b2j = this.b2j = {};
+            var populardict = {};
+            for (var i = 0; i < b.length; i++) {
+                var elt = b[i];
+                if (b2j.hasOwnProperty(elt)) {
+                    var indices = b2j[elt];
+                    if (n >= 200 && indices.length * 100 > n) {
+                        populardict[elt] = 1;
+                        delete b2j[elt];
+                    } else {
+                        indices.push(i);
+                    }
+                } else {
+                    b2j[elt] = [i];
+                }
+            }
+
+            for (var elt in populardict) {
+                if (populardict.hasOwnProperty(elt)) {
+                    delete b2j[elt];
+                }
+            }
+
+            var isjunk = this.isjunk;
+            var junkdict = {};
+            if (isjunk) {
+                for (var elt in populardict) {
+                    if (populardict.hasOwnProperty(elt) && isjunk(elt)) {
+                        junkdict[elt] = 1;
+                        delete populardict[elt];
+                    }
+                }
+                for (var elt in b2j) {
+                    if (b2j.hasOwnProperty(elt) && isjunk(elt)) {
+                        junkdict[elt] = 1;
+                        delete b2j[elt];
+                    }
+                }
+            }
+
+            this.isbjunk = difflib.__isindict(junkdict);
+            this.isbpopular = difflib.__isindict(populardict);
+        }
+
+        this.find_longest_match = function (alo, ahi, blo, bhi) {
+            var a = this.a;
+            var b = this.b;
+            var b2j = this.b2j;
+            var isbjunk = this.isbjunk;
+            var besti = alo;
+            var bestj = blo;
+            var bestsize = 0;
+            var j = null;
+
+            var j2len = {};
+            var nothing = [];
+            for (var i = alo; i < ahi; i++) {
+                var newj2len = {};
+                var jdict = difflib.__dictget(b2j, a[i], nothing);
+                for (var jkey in jdict) {
+                    if (jdict.hasOwnProperty(jkey)) {
+                        j = jdict[jkey];
+                        if (j < blo) continue;
+                        if (j >= bhi) break;
+                        newj2len[j] = k = difflib.__dictget(j2len, j - 1, 0) + 1;
+                        if (k > bestsize) {
+                            besti = i - k + 1;
+                            bestj = j - k + 1;
+                            bestsize = k;
+                        }
+                    }
+                }
+                j2len = newj2len;
+            }
+
+            while (besti > alo && bestj > blo && !isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
+                besti--;
+                bestj--;
+                bestsize++;
+            }
+
+            while (besti + bestsize < ahi && bestj + bestsize < bhi &&
+                    !isbjunk(b[bestj + bestsize]) &&
+                    a[besti + bestsize] == b[bestj + bestsize]) {
+                bestsize++;
+            }
+
+            while (besti > alo && bestj > blo && isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
+                besti--;
+                bestj--;
+                bestsize++;
+            }
+
+            while (besti + bestsize < ahi && bestj + bestsize < bhi && isbjunk(b[bestj + bestsize]) &&
+                    a[besti + bestsize] == b[bestj + bestsize]) {
+                bestsize++;
+            }
+
+            return [besti, bestj, bestsize];
+        }
+
+        this.get_matching_blocks = function () {
+            if (this.matching_blocks != null) return this.matching_blocks;
+            var la = this.a.length;
+            var lb = this.b.length;
+
+            var queue = [[0, la, 0, lb]];
+            var matching_blocks = [];
+            var alo, ahi, blo, bhi, qi, i, j, k, x;
+            while (queue.length) {
+                qi = queue.pop();
+                alo = qi[0];
+                ahi = qi[1];
+                blo = qi[2];
+                bhi = qi[3];
+                x = this.find_longest_match(alo, ahi, blo, bhi);
+                i = x[0];
+                j = x[1];
+                k = x[2];
+
+                if (k) {
+                    matching_blocks.push(x);
+                    if (alo < i && blo < j)
+                        queue.push([alo, i, blo, j]);
+                    if (i+k < ahi && j+k < bhi)
+                        queue.push([i + k, ahi, j + k, bhi]);
+                }
+            }
+
+            matching_blocks.sort(difflib.__ntuplecomp);
+
+            var i1 = j1 = k1 = block = 0;
+            var non_adjacent = [];
+            for (var idx in matching_blocks) {
+                if (matching_blocks.hasOwnProperty(idx)) {
+                    block = matching_blocks[idx];
+                    i2 = block[0];
+                    j2 = block[1];
+                    k2 = block[2];
+                    if (i1 + k1 == i2 && j1 + k1 == j2) {
+                        k1 += k2;
+                    } else {
+                        if (k1) non_adjacent.push([i1, j1, k1]);
+                        i1 = i2;
+                        j1 = j2;
+                        k1 = k2;
+                    }
+                }
+            }
+
+            if (k1) non_adjacent.push([i1, j1, k1]);
+
+            non_adjacent.push([la, lb, 0]);
+            this.matching_blocks = non_adjacent;
+            return this.matching_blocks;
+        }
+
+        this.get_opcodes = function () {
+            if (this.opcodes != null) return this.opcodes;
+            var i = 0;
+            var j = 0;
+            var answer = [];
+            this.opcodes = answer;
+            var block, ai, bj, size, tag;
+            var blocks = this.get_matching_blocks();
+            for (var idx in blocks) {
+                if (blocks.hasOwnProperty(idx)) {
+                    block = blocks[idx];
+                    ai = block[0];
+                    bj = block[1];
+                    size = block[2];
+                    tag = '';
+                    if (i < ai && j < bj) {
+                        tag = 'replace';
+                    } else if (i < ai) {
+                        tag = 'delete';
+                    } else if (j < bj) {
+                        tag = 'insert';
+                    }
+                    if (tag) answer.push([tag, i, ai, j, bj]);
+                    i = ai + size;
+                    j = bj + size;
+
+                    if (size) answer.push(['equal', ai, i, bj, j]);
+                }
+            }
+
+            return answer;
+        }
+
+        // this is a generator function in the python lib, which of course is not supported in javascript
+        // the reimplementation builds up the grouped opcodes into a list in their entirety and returns that.
+        this.get_grouped_opcodes = function (n) {
+            if (!n) n = 3;
+            var codes = this.get_opcodes();
+            if (!codes) codes = [["equal", 0, 1, 0, 1]];
+            var code, tag, i1, i2, j1, j2;
+            if (codes[0][0] == 'equal') {
+                code = codes[0];
+                tag = code[0];
+                i1 = code[1];
+                i2 = code[2];
+                j1 = code[3];
+                j2 = code[4];
+                codes[0] = [tag, Math.max(i1, i2 - n), i2, Math.max(j1, j2 - n), j2];
+            }
+            if (codes[codes.length - 1][0] == 'equal') {
+                code = codes[codes.length - 1];
+                tag = code[0];
+                i1 = code[1];
+                i2 = code[2];
+                j1 = code[3];
+                j2 = code[4];
+                codes[codes.length - 1] = [tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)];
+            }
+
+            var nn = n + n;
+            var groups = [];
+            for (var idx in codes) {
+                if (codes.hasOwnProperty(idx)) {
+                    code = codes[idx];
+                    tag = code[0];
+                    i1 = code[1];
+                    i2 = code[2];
+                    j1 = code[3];
+                    j2 = code[4];
+                    if (tag == 'equal' && i2 - i1 > nn) {
+                        groups.push([tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)]);
+                        i1 = Math.max(i1, i2-n);
+                        j1 = Math.max(j1, j2-n);
+                    }
+
+                    groups.push([tag, i1, i2, j1, j2]);
+                }
+            }
+
+            if (groups && groups[groups.length - 1][0] == 'equal') groups.pop();
+
+            return groups;
+        }
+
+        this.ratio = function () {
+            matches = difflib.__reduce(
+                            function (sum, triple) { return sum + triple[triple.length - 1]; },
+                            this.get_matching_blocks(), 0);
+            return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
+        }
+
+        this.quick_ratio = function () {
+            var fullbcount, elt;
+            if (this.fullbcount == null) {
+                this.fullbcount = fullbcount = {};
+                for (var i = 0; i < this.b.length; i++) {
+                    elt = this.b[i];
+                    fullbcount[elt] = difflib.__dictget(fullbcount, elt, 0) + 1;
+                }
+            }
+            fullbcount = this.fullbcount;
+
+            var avail = {};
+            var availhas = difflib.__isindict(avail);
+            var matches = numb = 0;
+            for (var i = 0; i < this.a.length; i++) {
+                elt = this.a[i];
+                if (availhas(elt)) {
+                    numb = avail[elt];
+                } else {
+                    numb = difflib.__dictget(fullbcount, elt, 0);
+                }
+                avail[elt] = numb - 1;
+                if (numb > 0) matches++;
+            }
+
+            return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
+        }
+
+        this.real_quick_ratio = function () {
+            var la = this.a.length;
+            var lb = this.b.length;
+            return _calculate_ratio(Math.min(la, lb), la + lb);
+        }
+
+        this.isjunk = isjunk ? isjunk : difflib.defaultJunkFunction;
+        this.a = this.b = null;
+        this.set_seqs(a, b);
+    }
+}
diff --git a/Source/devtools/front_end/nativeMemoryProfiler.css b/Source/devtools/front_end/nativeMemoryProfiler.css
new file mode 100644
index 0000000..3f609cb
--- /dev/null
+++ b/Source/devtools/front_end/nativeMemoryProfiler.css
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ *     * 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.
+ */
+
+.memory-pie-chart-container {
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+}
+
+.memory-pie-chart {
+    -webkit-flex: 1;
+}
+
+.memory-blocks-list .swatch {
+    background-image: none;
+}
+
+.memory-blocks-list {
+    padding: 1px;
+    font-size: 11px;
+}
+
+.memory-blocks-list .item {
+    margin: 10px;
+}
+
+.native-snapshot-view {
+    display: none;
+    overflow: hidden;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.native-snapshot-view.visible {
+    display: block;
+}
+
+.native-snapshot-view .data-grid {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    border: none;
+}
+
+.native-snapshot-view .data-grid td {
+    height: 14px;
+    padding: 1px 4px;
+}
+
+.native-snapshot-view .data-grid div.size-text {
+    float: left;
+    text-align: right;
+    width: 70px;
+    padding-right: 5px;
+}
+
+.native-snapshot-view .data-grid .dimmed div.size-bar {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(hsla(0, 100%, 100%, 0.7)), to(hsla(0, 100%, 100%, 0.5)));
+    color: hsla(0, 0%, 0%, 0.5);
+}
+
+.native-snapshot-view .data-grid div.size-bar {
+    text-align: center;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 255, 0.5)), to(rgba(192, 192, 192, 0)));
+    height: 14px;
+    border-radius: 2px;
+    color: #222;
+    white-space: normal;
+    min-width: 1px;
+}
+
+.native-snapshot-view .data-grid div.percent-text {
+    display: inline-block;
+}
diff --git a/Source/devtools/front_end/navigatorView.css b/Source/devtools/front_end/navigatorView.css
new file mode 100644
index 0000000..1da2440
--- /dev/null
+++ b/Source/devtools/front_end/navigatorView.css
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+.navigator-domain-tree-item .icon {
+    content: url(Images/domain.png);
+}
+
+.navigator-folder-tree-item .icon {
+    opacity: 0.7;
+    content: url(Images/frame.png);
+}
+
+.navigator-script-tree-item .icon {
+    content: url(Images/resourceJSIcon.png);
+}
+
+.navigator-stylesheet-tree-item .icon {
+    content: url(Images/resourceCSSIcon.png);
+}
+
+.navigator-document-tree-item .icon {
+    content: url(Images/resourceDocumentIcon.png);
+}
+
+.navigator-other-tree-item .icon {
+    content: url(Images/resourcePlainIcon.png);
+}
+
+.navigator > ol {
+    min-height: 100%;
+    display: inline-block;
+}
+
+.navigator li {
+    height: 18px;
+    line-height: 17px;
+    white-space: nowrap;
+}
+
+.navigator :focus li.selected {
+    color: white;
+}
+
+.navigator li.selected .selection {
+    height: 18px;
+}
+
+.navigator > ol.being-edited li.selected .selection {
+    background-color: rgb(56, 121, 217);
+}
+
+
+.navigator .icon {
+    width: 16px;
+    height: 16px;
+    float: left;
+}
+
+.navigator .base-navigator-tree-element-title {
+    display: inline-block;
+    position: relative;
+    padding-left: 2px;
+}
+
+.navigator .base-navigator-tree-element-title.editing {
+    margin: auto;
+}
+
+.navigator-tabbed-pane .tabbed-pane-content {
+    overflow: hidden;
+}
+
+.navigator-tabbed-pane .navigator-container {
+    overflow: auto;
+}
+
+.navigator-tabbed-pane .navigator {
+    padding-left: 0;
+}
+
+.navigator-tabbed-pane .tabbed-pane-header {
+    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#F5F5F5), to(#E5E5E5));
+}
+
+.navigator-tabbed-pane .tabbed-pane-header-contents {
+    margin-left: 2px;
+    margin-right: 28px;
+}
diff --git a/Source/devtools/front_end/networkLogView.css b/Source/devtools/front_end/networkLogView.css
new file mode 100644
index 0000000..0a0b537
--- /dev/null
+++ b/Source/devtools/front_end/networkLogView.css
@@ -0,0 +1,535 @@
+.network-log-grid.data-grid {
+    border: none;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.network-log-grid.data-grid table.data {
+    -webkit-background-size: 1px 82px;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), color-stop(0.5, rgba(0, 0, 0, 0)), color-stop(0.5, rgba(0, 0, 0, 0.05)), to(rgba(0, 0, 0, 0.05)));
+}
+
+.network-log-grid.data-grid.small table.data {
+    -webkit-background-size: 1px 42px;
+}
+
+.network-log-grid.data-grid td {
+    line-height: 17px;
+    height: 41px;
+    border-right: 1px solid rgb(210, 210, 210);
+    vertical-align: middle;
+}
+
+.network-log-grid.data-grid.small td {
+    height: 21px;
+}
+
+.network-log-grid.data-grid th {
+    border-bottom: 1px solid rgb(64%, 64%, 64%);
+    height: 30px;
+}
+
+.network-log-grid.data-grid.small th {
+    height: 22px;
+}
+
+.network-log-grid.data-grid th, .network-log-grid.data-grid th.sort-descending, .network-log-grid.data-grid th.sort-ascending {
+    background: -webkit-gradient(linear, left top, left bottom, from(rgb(236, 236, 236)), to(rgb(217, 217, 217)));
+}
+
+.network-log-grid.data-grid .data-container {
+    top: 31px;
+}
+
+.network-log-grid.data-grid.small .data-container {
+    top: 23px;
+}
+
+.network-log-grid.data-grid select {
+    -webkit-appearance: none;
+    background-color: transparent;
+    border: none;
+    width: 100%;
+    font-size: inherit;
+    color: inherit;
+}
+
+.network-log-grid.data-grid.small tr.offscreen {
+    height: 21px;
+}
+
+.network-log-grid.data-grid tr.offscreen {
+    height: 41px;
+}
+
+.network-log-grid.data-grid tr.offscreen > td > div {
+    display: none;
+}
+
+.network-log-grid.data-grid tr.filler {
+    background-color: white;
+}
+
+.network-log-grid.data-grid tr:not(.filler) td.name-column {
+    cursor: pointer;
+}
+
+#network-container:not(.brief-mode) .network-log-grid.data-grid td.name-column:hover {
+    text-decoration: underline;
+}
+
+.network-log-grid.data-grid.small .network-graph-side {
+    height: 14px;
+}
+
+.network-log-grid.data-grid th.sortable:active {
+    background-image: none !important;
+}
+
+.network-cell-subtitle {
+    font-weight: normal;
+    color: gray;
+}
+
+.network-error-row, .network-error-row .network-cell-subtitle {
+    color: rgb(230, 0, 0);
+}
+
+.initiator-column a {
+    color: inherit;
+}
+
+.network-log-grid.data-grid tr.selected .network-cell-subtitle {
+    color: white;
+}
+
+.network-log-grid tr.highlighted-row {
+    -webkit-animation: "network-row-highlight-fadeout" 2s 0s;
+}
+
+@-webkit-keyframes network-row-highlight-fadeout {
+    from {background-color: rgba(56, 121, 217, 1); }
+    to { background-color: rgba(56, 121, 217, 0); }
+}
+
+.network-header-subtitle {
+    color: gray;
+}
+
+.network-log-grid.data-grid.small .network-cell-subtitle,
+.network-log-grid.data-grid.small .network-header-subtitle
+{
+    display: none;
+}
+
+/* Resource preview icons */
+
+.network-log-grid.data-grid .icon {
+    content: url(Images/resourcePlainIcon.png);
+}
+
+.network-log-grid.data-grid.small .icon {
+    content: url(Images/resourcePlainIconSmall.png);
+}
+
+.network-log-grid.data-grid .network-type-script .icon {
+    content: url(Images/resourceJSIcon.png);
+}
+
+.network-log-grid.data-grid.small .network-type-script .icon {
+    content: url(Images/resourceDocumentIconSmall.png);
+}
+
+.network-log-grid.data-grid .network-type-document .icon {
+    content: url(Images/resourceDocumentIcon.png);
+}
+
+.network-log-grid.data-grid.small .network-type-document .icon {
+    content: url(Images/resourceDocumentIconSmall.png);
+}
+
+.network-log-grid.data-grid .network-type-stylesheet .icon {
+    content: url(Images/resourceCSSIcon.png);
+}
+
+.network-log-grid.data-grid.small .network-type-stylesheet .icon {
+    content: url(Images/resourceDocumentIconSmall.png);
+}
+
+.network-log-grid.data-grid .network-type-image .icon {
+    position: relative;
+    background-image: url(Images/resourcePlainIcon.png);
+    background-repeat: no-repeat;
+    content: "";
+}
+
+.network-log-grid.data-grid.small .network-type-image .icon {
+    background-image: url(Images/resourcePlainIconSmall.png);
+    content: "";
+}
+
+.network-log-grid.data-grid .icon {
+    float: left;
+    width: 32px;
+    height: 32px;
+    margin-top: 1px;
+    margin-right: 3px;
+}
+
+.network-log-grid.data-grid.small .icon {
+    width: 16px;
+    height: 16px;
+}
+
+.network-log-grid.data-grid .image-network-icon-preview {
+    position: absolute;
+    margin: auto;
+    top: 3px;
+    bottom: 4px;
+    left: 5px;
+    right: 5px;
+    max-width: 18px;
+    max-height: 21px;
+    min-width: 1px;
+    min-height: 1px;
+}
+
+.network-log-grid.data-grid.small .image-network-icon-preview {
+    top: 2px;
+    bottom: 1px;
+    left: 3px;
+    right: 3px;
+    max-width: 8px;
+    max-height: 11px;
+}
+
+/* Graph styles */
+
+.network-graph-side {
+    position: relative;
+    height: 36px;
+    padding: 0;
+    white-space: nowrap;
+    margin-top: 1px;
+    border-top: 1px solid transparent;
+    overflow: hidden;
+}
+
+.network-graph-bar-area {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+}
+
+.network-graph-bar-area,
+.network-timeline-grid .resources-dividers,
+.network-timeline-grid .resources-event-dividers,
+.network-timeline-grid .resources-dividers-label-bar {
+    right: 12px;
+    left: 12px;
+}
+
+.network-graph-label {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    margin: auto -7px;
+    height: 13px;
+    line-height: 13px;
+    font-size: 90%;
+    color: rgba(0, 0, 0, 0.75);
+    text-shadow: rgba(255, 255, 255, 0.25) 1px 0 0, rgba(255, 255, 255, 0.25) -1px 0 0, rgba(255, 255, 255, 0.333) 0 1px 0, rgba(255, 255, 255, 0.25) 0 -1px 0;
+    z-index: 150;
+    overflow: hidden;
+    text-align: center;
+    opacity: 0;
+    -webkit-transition: opacity 250ms ease-in-out;
+}
+
+.network-graph-side:hover .network-graph-label {
+    opacity: 1;
+}
+
+.network-graph-label:empty {
+    display: none;
+}
+
+.network-graph-label.waiting {
+    margin-right: 5px;
+}
+
+.network-graph-label.waiting-right {
+    margin-left: 5px;
+}
+
+.network-graph-label.before {
+    color: rgba(0, 0, 0, 0.7);
+    text-shadow: none;
+    text-align: right;
+    margin-right: 2px;
+}
+
+.network-graph-label.before::after {
+    padding-left: 2px;
+    height: 6px;
+    content: url(Images/graphLabelCalloutLeft.png);
+}
+
+.network-graph-label.after {
+    color: rgba(0, 0, 0, 0.7);
+    text-shadow: none;
+    text-align: left;
+    margin-left: 2px;
+}
+
+.network-graph-label.after::before {
+    padding-right: 2px;
+    height: 6px;
+    content: url(Images/graphLabelCalloutRight.png);
+}
+
+.network-graph-bar {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    margin: auto -7px;
+    border-width: 6px 7px;
+    height: 0;
+    min-width: 14px;
+    opacity: 0.65;
+    -webkit-border-image: url(Images/timelinePillGray.png) 7 7 7 7;
+}
+
+.network-graph-bar.waiting, .network-graph-bar.waiting-right {
+    opacity: 0.35;
+}
+
+/* Resource categories */
+
+
+.resource-cached .network-graph-bar {
+    -webkit-border-image: url(Images/timelineHollowPillGray.png) 7 7 7 7;
+}
+
+.network-type-document .network-graph-bar {
+    -webkit-border-image: url(Images/timelinePillBlue.png) 7 7 7 7;
+}
+
+.network-type-document.resource-cached .network-graph-bar {
+    -webkit-border-image: url(Images/timelineHollowPillBlue.png) 7 7 7 7;
+}
+
+.network-type-stylesheet .network-graph-bar {
+    -webkit-border-image: url(Images/timelinePillGreen.png) 7 7 7 7;
+}
+
+.network-type-stylesheet.resource-cached .network-graph-bar {
+    -webkit-border-image: url(Images/timelineHollowPillGreen.png) 7 7 7 7;
+}
+
+.network-type-image .network-graph-bar {
+    -webkit-border-image: url(Images/timelinePillPurple.png) 6 7 6 7;
+}
+
+.network-type-image.resource-cached .network-graph-bar {
+    border-image: url(Images/timelineHollowPillPurple.png) 7 7 7 7;
+}
+
+.network-type-font .network-graph-bar {
+    -webkit-border-image: url(Images/timelinePillRed.png) 7 7 7 7;
+}
+
+.network-type-font.resource-cached .network-graph-bar {
+    -webkit-border-image: url(Images/timelineHollowPillRed.png) 7 7 7 7;
+}
+
+.network-type-script .network-graph-bar {
+    -webkit-border-image: url(Images/timelinePillOrange.png) 7 7 7 7;
+}
+
+.network-type-script.resource-cached .network-graph-bar {
+    -webkit-border-image: url(Images/timelineHollowPillOrange.png) 7 7 7 7;
+}
+
+.network-type-xhr .network-graph-bar {
+    -webkit-border-image: url(Images/timelinePillYellow.png) 7 7 7 7;
+}
+
+.network-type-xhr.resource-cached .network-graph-bar {
+    -webkit-border-image: url(Images/timelineHollowPillYellow.png) 7 7 7 7;
+}
+
+.network-type-websocket .network-graph-bar {
+    -webkit-border-image: url(Images/timelinePillGray.png) 7 7 7 7;
+}
+
+.network-type-websocket.resource-cached .network-graph-bar {
+   -webkit-border-image: url(Images/timelineHollowPillGray.png) 7 7 7 7;
+}
+
+.network-dim-cell {
+    color: grey;
+}
+
+/* Dividers */
+
+.network-timeline-grid {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 14px; /* Keep in sync with td.corner width */
+    pointer-events: none;
+}
+
+.network-log-grid.data-grid .timeline-column,
+.network.panel .data-grid th.timeline-column {
+    border-right: none;
+}
+
+.data-grid td.timeline-column {
+    padding-left: 0;
+    padding-right: 0;
+}
+
+.network-event-divider-padding {
+    position: absolute;
+    width: 8px;
+    top: 0;
+    bottom: 0;
+    pointer-events: auto;
+}
+
+.network-event-divider {
+    position: absolute;
+    width: 2px;
+    top: 31px;
+    bottom: 0;
+    z-index: 300;
+}
+
+.network-timeline-grid.small .network-event-divider {
+    top: 23px;
+}
+
+.network-red-divider {
+    background-color: rgba(255, 0, 0, 0.5);
+}
+
+.network-blue-divider {
+    background-color: rgba(0, 0, 255, 0.5);
+}
+
+.network-log-grid.data-grid .resources-dividers {
+    z-index: 0;
+}
+
+.network-log-grid.data-grid .resources-dividers-label-bar {
+    background-color: transparent;
+    border: none;
+    height: 30px;
+    pointer-events: none;
+}
+
+.network-timeline-grid.small .resources-dividers-label-bar {
+    height: 23px;
+}
+
+.network-timeline-grid .resources-divider-label {
+    top: 0;
+    margin-top: -5px;
+}
+
+.network-timeline-grid .resources-dividers-label-bar .resources-divider {
+    top: 23px;
+}
+
+.network-timeline-grid.small .resources-dividers-label-bar .resources-divider {
+    top: 15px;
+}
+
+.network-timeline-grid .resources-divider:first-child .resources-divider-label {
+    display: none;
+}
+
+.network-timeline-grid .resources-dividers-label-bar .resources-divider:first-child {
+    background-color: transparent;
+}
+
+/* Filters */
+
+.network-log-grid.data-grid table.data tr.revealed.network-item.filtered-out {
+    display: none;
+}
+
+/* Summary */
+
+.network-log-grid.data-grid tr.filler td {
+    padding-bottom: 20px !important;
+}
+
+.network-log-grid.data-grid .network-summary-bar {
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-color: rgb(101, 111, 130);
+}
+
+.network-log-grid.data-grid .network-summary-bar td {
+    color: white;
+    height: 20px !important;
+    border: none;
+    font-size: 110%;
+    padding: 0 0 0 8px;
+    white-space: pre;
+    overflow : hidden;
+    text-overflow : ellipsis;
+}
+
+.network-log-grid.data-grid .network-summary-bar .warning-icon-small {
+    margin-right: 8px;
+}
+
+#network-container {
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    width: 100%;
+    height: 100%;
+    border-right: 0 none transparent;
+    overflow-y: auto;
+    overflow-x: hidden;
+}
+
+// Brief mode peculiarities.
+#network-container.brief-mode .network-timeline-grid {
+    display: none;
+}
+
+#network-container.brief-mode td,
+#network-container.brief-mode th {
+    border-right: none;
+}
+
+#network-container.brief-mode .network-log-grid.data-grid .data-grid-resizer {
+    display: none;
+}
+
+#network-container.brief-mode .network-timeline-grid {
+    display: none;
+}
+
+#network-container:not(.brief-mode) .data-grid tr.selected {
+    background-color: transparent;
+    color: #222;
+}
+
+#network-container.brief-mode .data-grid .data-container {
+    padding-right: 0;
+}
diff --git a/Source/devtools/front_end/networkPanel.css b/Source/devtools/front_end/networkPanel.css
new file mode 100644
index 0000000..93df316
--- /dev/null
+++ b/Source/devtools/front_end/networkPanel.css
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+.network-larger-resources-status-bar-item .glyph {
+    -webkit-mask-position: -224px 0;
+}
+
+#network-views {
+    background: rgb(203, 203, 203);
+}
+
+#network-close-button {
+    position: absolute;
+    top: 8px;
+    left: 5px;
+    z-index: 10;
+    display: none;
+}
+
+#network-views.small #network-close-button {
+    top: 4px;
+}
+
+.network.panel.viewing-resource #network-close-button  {
+    display: block;
+}
+
+.network.panel .sidebar {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    width: auto;
+    border: none;
+    background: inherit;
+}
+
+.network.panel:not(.viewing-resource) .sidebar-resizer-vertical {
+    display: none;
+}
+
+.network.panel .scope-bar {
+    height: 23px;
+    padding-top: 5px;
+}
+
+.network.panel .data-grid th.sort-descending, .network.panel .data-grid th.sort-ascending {
+    background: -webkit-gradient(linear, left top, left bottom, from(rgb(236, 236, 236)), to(rgb(217, 217, 217)));
+}
+
+#network-views .network-item-view .tabbed-pane-header {
+    height: 31px;
+    padding-top: 8px;
+    padding-left: 13px;
+    white-space: nowrap;
+}
+
+#network-views.small .network-item-view .tabbed-pane-header {
+    height: 23px;
+    padding-top: 0;
+}
+
+.network-item-view {
+    display: none;
+    position: absolute;
+    background: white;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.network-item-view.visible {
+    display: -webkit-flex;
+}
+
+.network-item-view .tabbed-pane-header {
+    background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(236, 236, 236)), to(rgb(217, 217, 217)));
+    border-bottom: 1px solid rgb(163, 163, 163);
+}
+
+.network-item-view .scope-bar li {
+    border-bottom-left-radius: 0;
+    border-bottom-right-radius: 0;
+}
+
+.resource-headers-view {
+    display: none;
+    margin: 6px;
+    -webkit-user-select: text;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow: auto;
+}
+
+.resource-headers-view.visible {
+    display: block;
+}
+
+.resource-headers-view .outline-disclosure .parent {
+    -webkit-user-select: none;
+    font-weight: bold;
+}
+
+.resource-headers-view .outline-disclosure .parent .section * {
+    font-weight: normal;
+}
+
+.resource-headers-view .outline-disclosure .children li {
+    white-space: nowrap;
+}
+
+.resource-headers-view .outline-disclosure li.expanded .header-count {
+    display: none;
+}
+
+.resource-headers-view .outline-disclosure li .header-toggle {
+    display: none;
+}
+
+.resource-headers-view .outline-disclosure li .status-from-cache {
+    color: gray;
+}
+
+.resource-headers-view .outline-disclosure li.expanded .header-toggle {
+    display: inline;
+    margin-left: 30px;
+    font-weight: normal;
+    color: rgb(45%, 45%, 45%);
+}
+
+.resource-headers-view .outline-disclosure li .header-toggle:hover {
+    color: rgb(20%, 20%, 45%);
+    cursor: pointer;
+}
+
+.resource-headers-view .outline-disclosure .header-name {
+    color: rgb(33%, 33%, 33%);
+    display: inline-block;
+    margin-right: 0.5em;
+    font-weight: bold;
+    vertical-align: top;
+    white-space: pre-wrap;
+}
+
+.resource-headers-view .outline-disclosure .header-value {
+    display: inline;
+    margin-right: 100px;
+    white-space: pre-wrap;
+    word-break: break-all;
+    margin-top: 1px;
+}
+
+.resource-cookies-view {
+    display: none;
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    bottom: 0;
+    overflow: auto;
+    margin: 12px;
+    height: 100%;
+}
+
+.resource-cookies-view.visible {
+    display: block;
+}
+
+.resource-cookies-view .data-grid {
+    height: 100%;
+}
+
+.resource-cookies-view .data-grid .row-group {
+    font-weight: bold;
+    font-size: 11px;
+}
+
+.resource-timing-view {
+    display: none;
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    bottom: 0;
+    margin: 6px;
+    font-weight: bold;
+    font-size: 11px;
+    color: rgb(30%, 30%, 30%);
+}
+
+.resource-timing-view table {
+    border-spacing: 21px 0;
+}
+
+.resource-timing-view .network-timing-bar {
+    opacity: 1;
+}
+
+.resource-timing-view .network-timing-bar.proxy {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(239, 228, 176)), to(rgb(139, 128, 76)));
+    border-left: 1px solid rgb(139, 128, 76);
+}
+
+.resource-timing-view .network-timing-bar.dns {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(153, 208, 216)), to(rgb(81, 174, 189)));
+    border-left: 1px solid rgb(81, 174, 189);
+}
+
+.resource-timing-view .network-timing-bar.connecting {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(203, 232, 145)), to(rgb(160, 214, 56)));
+    border-left: 1px solid rgb(160, 214, 56);
+}
+
+.resource-timing-view .network-timing-bar.ssl {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(21, 232, 145)), to(rgb(216, 149, 132)));
+    border-left: 1px solid rgb(216, 149, 132);
+}
+
+.resource-timing-view .network-timing-bar.sending {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(232, 192, 182)), to(rgb(216, 147, 130)));
+    border-left: 1px solid rgb(216, 147, 130);
+}
+
+.resource-timing-view .network-timing-bar.waiting {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(188, 179, 208)), to(rgb(141, 125, 175)));
+    border-left: 1px solid rgb(141, 125, 175);
+}
+
+.resource-timing-view .network-timing-bar.receiving {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(214, 214, 214)), to(rgb(182, 182, 182)));
+    border-left: 1px solid rgb(182, 182, 182);
+}
+
+.resource-timing-view.visible {
+    display: block;
+}
+
+.resource-websocket {
+    -webkit-user-select: text;
+}
+
+.resource-websocket, .resource-websocket .data-grid {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    border: none;
+}
+
+.resource-websocket .data-grid .data {
+    background-image: none;
+}
+
+.resource-websocket td {
+    padding-top: 3px;
+    padding-bottom: 3px;
+    border-top: 1px solid rgb(240, 240, 240);
+}
+
+.resource-websocket .data-column div {
+    word-break: break-all;
+    white-space: pre-wrap;
+}
+
+.resource-websocket-row-outcoming {
+    background-color: rgb(226, 247, 218);
+}
+
+.resource-websocket-row-outcoming:not(.selected) td {
+    border-right-color: rgb(177, 209, 165);
+}
+
+.resource-websocket-row-outcoming:not(.selected) td, .resource-websocket-row-outcoming:not(.selected) + tr td {
+    border-top-color: rgb(199, 236, 185);
+}
+
+.resource-websocket-row-opcode {
+    background-color: rgb(255, 255, 232);
+    color: rgb(170, 111, 71);
+}
+
+.resource-websocket-row-opcode td {
+    border-color: rgb(211, 187, 171);
+}
+
+.resource-websocket-row-opcode td, .resource-websocket-row-opcode + tr td {
+    border-top-color: rgb(248, 240, 210);
+}
+
+.resource-websocket-row-error {
+    background-color: rgb(255, 237, 237);
+    color: rgb(182, 0, 0);
+}
diff --git a/Source/devtools/front_end/panelEnablerView.css b/Source/devtools/front_end/panelEnablerView.css
new file mode 100644
index 0000000..edecfa9
--- /dev/null
+++ b/Source/devtools/front_end/panelEnablerView.css
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+.panel-enabler-view {
+    z-index: 1000;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-color: white;
+    font-size: 13px;
+    text-align: center;
+    overflow-x: hidden;
+    overflow-y: overlay;
+    display: none;
+}
+
+.panel-enabler-view.visible {
+    display: block;
+}
+
+.panel-enabler-view .panel-enabler-view-content {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    max-height: 390px;
+    margin: auto;
+    white-space: nowrap;
+}
+
+.panel-enabler-view h1 {
+    color: rgb(110, 116, 128);
+    font-size: 16px;
+    line-height: 20px;
+    font-weight: normal;
+    margin-top: 0;
+}
+
+.panel-enabler-disclaimer {
+    font-size: 10px;
+    color: rgb(110, 116, 128);
+    margin-bottom: 12px;
+    margin-left: 20px;
+}
+
+.panel-enabler-disclaimer:empty {
+    display: none;
+}
+
+.panel-enabler-view img {
+    height: 100%;
+    min-height: 200px;
+    max-width: 100%;
+    top: 0;
+    bottom: 0;
+    padding: 20px 0 20px 20px;
+    margin: auto;
+    vertical-align: middle;
+}
+
+.panel-enabler-view img.hidden {
+    display: initial !important;
+    width: 0;
+}
+
+.panel-enabler-view .flexible-space {
+    -webkit-flex: 1;
+}
+
+.panel-enabler-view form {
+    display: inline-block;
+    vertical-align: middle;
+    width: 330px;
+    margin: 0;
+    padding: 15px;
+    white-space: normal;
+}
+
+.panel-enabler-view label {
+    position: relative;
+    display: block;
+    text-align: left;
+    word-break: break-word;
+    margin: 0 0 5px 20px;
+}
+
+.panel-enabler-view button:not(.status-bar-item) {
+    font-size: 13px;
+    margin: 6px 0 0 0;
+    padding: 3px 20px;
+    height: 24px;
+    color: rgb(6, 6, 6);
+    border: 1px solid rgb(165, 165, 165);
+    background-color: rgb(237, 237, 237);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223)));
+    -webkit-border-radius: 12px;
+    -webkit-appearance: none;
+}
+
+body.inactive .panel-enabler-view button:not(.status-bar-item), .panel-enabler-view button:disabled:not(.status-bar-item) {
+    color: rgb(130, 130, 130);
+    border-color: rgb(212, 212, 212);
+    background-color: rgb(239, 239, 239);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(250, 250, 250)), to(rgb(235, 235, 235)));
+}
+
+.panel-enabler-view button:active:not(.status-bar-item) {
+    background-color: rgb(215, 215, 215);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239)));
+}
+
+.panel-enabler-view input[type="radio"] {
+    height: 17px;
+    width: 17px;
+    border: 1px solid rgb(165, 165, 165);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223)));
+    -webkit-border-radius: 8px;
+    -webkit-appearance: none;
+    vertical-align: middle;
+    margin: 0 5px 5px 0;
+}
+
+.panel-enabler-view input[type="radio"]:active:not(:disabled) {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239)));
+}
+
+.panel-enabler-view input[type="radio"]:checked {
+    background: url(Images/radioDot.png) center no-repeat,
+                -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223)));
+}
+
+.panel-enabler-view input[type="radio"]:checked:active {
+    background: url(Images/radioDot.png) center no-repeat,
+                -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239)));
+}
diff --git a/Source/devtools/front_end/popover.css b/Source/devtools/front_end/popover.css
new file mode 100644
index 0000000..8bb6ebc
--- /dev/null
+++ b/Source/devtools/front_end/popover.css
@@ -0,0 +1,126 @@
+.popover {
+    position: absolute;
+    -webkit-border-image: url(Images/popoverBackground.png) 25 25 25 25;
+    border-width: 25px;
+    z-index: 100;
+    pointer-events: none;
+}
+
+.popover .content {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    pointer-events: auto;
+    overflow: auto;
+    -webkit-user-select: text;
+    line-height: 11px;
+}
+
+.popover .content.fixed-height {
+    overflow: hidden;
+} 
+
+.popover .arrow {
+    position: absolute;
+    background-image: url(Images/popoverArrows.png);
+    width: 19px;
+    height: 19px;
+    margin-left: 15px;
+    margin-top: -25px;
+    top: 0;
+    left: 0;
+}
+
+.popover.top-left-arrow .arrow {
+    /* The default is top-left, no styles needed. */
+}
+
+.popover.top-right-arrow .arrow {
+    right: 25px;
+    left: auto;
+}
+
+.popover.bottom-left-arrow .arrow {
+    top: auto;
+    bottom: 0;
+    margin-top: 0;
+    margin-bottom: -25px;
+    background-position: 0 -19px;
+}
+
+.popover.bottom-right-arrow .arrow {
+    right: 15px;
+    left: auto;
+    top: auto;
+    bottom: 0;
+    margin-top: 0;
+    margin-bottom: -25px;
+    background-position: 0 -19px;
+}
+
+.popover.left-top-arrow .arrow {
+    top: 0;
+    margin-top: 15px;
+    margin-left: -25px;
+    background-position: 0 -38px;
+}
+
+.popover.left-bottom-arrow .arrow {
+    top: auto;
+    bottom: 0;
+    margin-bottom: 15px;
+    margin-left: -25px;
+    background-position: 0 -38px;
+}
+
+.popover.right-top-arrow .arrow {
+    right: 0;
+    left: auto;
+    top: 0;
+    margin-top: 15px;
+    margin-right: -25px;
+    background-position: 0 -57px;
+}
+
+.popover.right-bottom-arrow .arrow {
+    right: 0;
+    left: auto;
+    top: auto;
+    bottom: 0;
+    margin-bottom: 15px;
+    margin-right: -25px;
+    background-position: 0 -57px;
+}
+
+.popover-details {
+    -webkit-user-select: text;
+    vertical-align: top;
+}
+
+.popover-function-name {
+    text-align: right;
+}
+
+.popover-stacktrace-title {
+    padding-top: 4px;
+}
+
+.popover-details-row-title {
+    font-weight: bold;
+    text-align: right;
+    white-space: nowrap;
+}
+
+.popover-details-row-data {
+    white-space: nowrap;
+}
+
+.popover-details-title {
+    border-bottom: 1px solid #B8B8B8;
+    font-weight: bold;
+    padding-bottom: 5px;
+    padding-top: 0px;
+    white-space: nowrap;
+}
diff --git a/Source/devtools/front_end/profilesPanel.css b/Source/devtools/front_end/profilesPanel.css
new file mode 100644
index 0000000..0ae6ff1
--- /dev/null
+++ b/Source/devtools/front_end/profilesPanel.css
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+/* Profiler Style */
+
+#profile-views {
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    bottom: 0;
+}
+
+.status-bar-items {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 200px;
+    overflow: hidden;
+    border-left: 1px solid rgb(184, 184, 184);
+    margin-left: -1px;
+}
+
+.profile-launcher-view-tree-item > .icon {
+    padding: 15px;
+    background-image: url(Images/toolbarIcons.png);
+    background-position-x: -160px;
+}
+
+.profile-sidebar-tree-item .icon {
+    content: url(Images/profileIcon.png);
+}
+
+.profile-sidebar-tree-item.small .icon {
+    content: url(Images/profileSmallIcon.png);
+}
+
+.profile-group-sidebar-tree-item .icon {
+    content: url(Images/profileGroupIcon.png);
+}
+
+.profile-view {
+    display: none;
+    overflow: hidden;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.profile-view.visible {
+    display: block;
+}
+
+.profile-view .data-grid {
+    border: none;
+    height: 100%;
+}
+
+.profile-view .data-grid th.average-column {
+    text-align: center;
+}
+
+.profile-view .data-grid td.average-column {
+    text-align: right;
+}
+
+.profile-view .data-grid th.self-column {
+    text-align: center;
+}
+
+.profile-view .data-grid td.self-column {
+    text-align: right;
+}
+
+.profile-view .data-grid th.total-column {
+    text-align: center;
+}
+
+.profile-view .data-grid td.total-column {
+    text-align: right;
+}
+
+.profile-view .data-grid .calls-column {
+    text-align: center;
+}
+
+.profile-node-file {
+    float: right;
+    color: gray;
+    margin-top: -1px;
+}
+
+.data-grid tr.selected .profile-node-file {
+    color: rgb(33%, 33%, 33%);
+}
+
+.data-grid:focus tr.selected .profile-node-file {
+    color: white;
+}
+
+.percent-time-status-bar-item .glyph {
+    -webkit-mask-position: -192px -24px;
+}
+
+.focus-profile-node-status-bar-item .glyph {
+    -webkit-mask-position: -96px 0;
+}
+
+.exclude-profile-node-status-bar-item .glyph {
+    -webkit-mask-position: -128px 0;
+}
+
+.reset-profile-status-bar-item .glyph {
+    -webkit-mask-position: 0 0;
+}
+
+.garbage-collect-status-bar-item .glyph {
+    -webkit-mask-position: -128px -24px;
+}
+
+.profile-launcher-view-content {
+    padding: 0 16px;
+    text-align: left;
+}
+
+.control-profiling {
+    -webkit-align-self: flex-start;
+}
+
+.profile-launcher-view-content h1 {
+    padding: 15px 0 10px;
+}
+
+.panel-enabler-view.profile-launcher-view form {
+    padding: 0;
+    font-size: 13px;
+    width: 100%;
+}
+
+.panel-enabler-view.profile-launcher-view label {
+    margin: 0;
+}
+
+.profile-launcher-view-content p {
+    color: grey;
+    margin-top: 1px;
+    margin-left: 22px;
+}
+
+.panel-enabler-view.profile-launcher-view button:not(.status-bar-item) {
+    color: rgb(6, 6, 6);
+    margin: 0 0 16px;
+}
+
+.profile-launcher-view-content button.running:not(.status-bar-item) {
+    color: red;
+}
+
+body.inactive .profile-launcher-view-content button.running:not(.status-bar-item) {
+    color: rgb(220, 130, 130);
+}
+
+.highlighted-row {
+    -webkit-animation: "row_highlight" 2s 0s;
+}
+
+@-webkit-keyframes row_highlight {
+    from {background-color: rgba(255, 255, 120, 1); }
+    to { background-color: rgba(255, 255, 120, 0); }
+}
+
+.profile-canvas-decoration .warning-icon-small {
+    margin-right: 4px;
+}
+.profile-canvas-decoration {
+    color: red;
+    margin: -14px 0 13px 22px;
+    padding-left: 14px;
+}
+
+.profile-canvas-decoration button {
+    margin: 0 0 0 10px !important;
+}
diff --git a/Source/devtools/front_end/resourceView.css b/Source/devtools/front_end/resourceView.css
new file mode 100644
index 0000000..26fef9f
--- /dev/null
+++ b/Source/devtools/front_end/resourceView.css
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+.resource-view {
+    display: none;
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    bottom: 0;
+    overflow: auto;
+}
+
+.resource-view.visible {
+    display: block;
+}
+
+.resource-view.font {
+    font-size: 60px;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+    text-align: center;
+    padding: 15px;
+}
+
+.resource-view .script-view {
+    background-color: rgb(240, 240, 240);
+}
+
+.resource-view.image > .image {
+    padding: 20px 20px 10px 20px;
+    text-align: center;
+}
+
+.resource-view.image > .info {
+    padding-bottom: 10px;
+    font-size: 11px;
+    -webkit-user-select: text;
+}
+
+.resource-view.image img.resource-image-view {
+    max-width: 100%;
+    max-height: 1000px;
+    background-image: url(Images/checker.png);
+    -webkit-box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.5);
+    -webkit-user-select: text;
+    -webkit-user-drag: auto;
+}
+
+.resource-status-image {
+    margin-top: -2px;
+    margin-right: 3px;
+    vertical-align: middle;
+}
+
+.resource-view.image .title {
+    text-align: center;
+    font-size: 13px;
+}
+
+.resource-view.image .infoList {
+    margin: 0;
+}
+
+.resource-view.image .infoList dt {
+    font-weight: bold;
+    display: inline-block;
+    width: 50%;
+    text-align: right;
+    color: rgb(76, 76, 76);
+}
+
+.resource-view.image .infoList dd {
+    display: inline-block;
+    padding-left: 8px;
+    width: 50%;
+    text-align: left;
+    margin: 0;
+}
+
+.resource-view.image .infoList dd::after {
+    white-space: pre;
+    content: "\A";
+}
+
+.script-view-fallback {
+    word-wrap: break-word;
+    -webkit-user-select: text;
+    background-color: inherit;
+}
\ No newline at end of file
diff --git a/Source/devtools/front_end/resourcesPanel.css b/Source/devtools/front_end/resourcesPanel.css
new file mode 100644
index 0000000..a299b9b
--- /dev/null
+++ b/Source/devtools/front_end/resourcesPanel.css
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+.resources.panel .sidebar-resizer-vertical {
+    top: 23px;
+}
+
+.resources.panel .sidebar {
+    padding-left: 0;
+    z-index: 10;
+}
+
+.resources.panel .sidebar li {
+    height: 18px;
+    white-space: nowrap;
+}
+
+.resources.panel .sidebar li.selected {
+    color: white;
+    text-shadow: rgba(0, 0, 0, 0.33) 1px 1px 0;
+}
+
+.resources.panel .sidebar li.selected .selection {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(162, 177, 207)), to(rgb(120, 138, 177)));
+    border-top: 1px solid #979797;
+    height: 18px;
+}
+
+.resources.panel .sidebar :focus li.selected .selection {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(92, 147, 213)), to(rgb(21, 83, 170)));
+    border-top: 1px solid rgb(68, 128, 200);
+}
+
+body.inactive .resources.panel .sidebar li.selected .selection {
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(180, 180, 180)), to(rgb(138, 138, 138)));
+    border-top: 1px solid rgb(151, 151, 151);
+}
+
+.resources.panel .sidebar .icon {
+    width: 16px;
+    height: 16px;
+    float: left;
+}
+
+.resources.panel .base-storage-tree-element-title {
+    overflow: hidden;
+    position: relative;
+    text-overflow: ellipsis;
+    padding-left: 2px;
+    top: 1px;
+}
+
+li.selected .base-storage-tree-element-subtitle {
+    color: white;
+}
+
+.base-storage-tree-element-subtitle {
+    padding-left: 2px;
+    color: rgb(80, 80, 80);
+    text-shadow: none;
+}
+
+.resources.panel .status {
+    float: right;
+    height: 16px;
+    margin-top: 1px;
+    margin-left: 4px;
+    line-height: 1em;
+}
+
+.resources.panel li .status .bubble {
+    height: 13px;
+    padding-top: 0;
+}
+
+.storage-view {
+    display: none;
+    overflow: hidden;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+
+.storage-view.visible {
+    display: block;
+}
+
+.storage-view {
+    overflow: hidden;
+}
+
+.storage-view .data-grid:not(.inline) {
+    border: none;
+    height: 100%;
+}
+
+.storage-view .storage-table-error {
+    color: rgb(66%, 33%, 33%);
+}
+
+.storage-view.query {
+    padding: 2px 0;
+    overflow-y: overlay;
+    overflow-x: hidden;
+}
+
+.database-query-prompt {
+    position: relative;
+    padding: 1px 22px 1px 24px;
+    min-height: 16px;
+    white-space: pre-wrap;
+    -webkit-user-modify: read-write-plaintext-only;
+    -webkit-user-select: text;
+}
+
+.database-user-query::before, .database-query-prompt::before, .database-query-result::before {
+    position: absolute;
+    display: block;
+    content: "";
+    left: 7px;
+    top: 0.8em;
+    width: 10px;
+    height: 10px;
+    margin-top: -7px;
+    -webkit-user-select: none;
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.database-user-query::before, .database-query-prompt::before, .database-query-result::before {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.database-query-prompt::before {
+    background-position: -192px -96px;
+}
+
+.database-user-query {
+    position: relative;
+    border-bottom: 1px solid rgb(245, 245, 245);
+    padding: 1px 22px 1px 24px;
+    min-height: 16px;
+}
+
+.database-user-query::before {
+    background-position: -192px -107px;
+}
+
+.database-query-text {
+    color: rgb(0, 128, 255);
+    -webkit-user-select: text;
+}
+
+.database-query-result {
+    position: relative;
+    padding: 1px 22px 1px 24px;
+    min-height: 16px;
+    margin-left: -24px;
+    padding-right: 0;
+}
+
+.database-query-result.error {
+    color: red;
+    -webkit-user-select: text;
+}
+
+.database-query-result.error::before {
+    background-position: -213px -96px;
+}
+
+.resource-sidebar-tree-item .icon {
+    content: url(Images/resourcePlainIcon.png);
+}
+
+.children.small .resource-sidebar-tree-item .icon {
+    content: url(Images/resourcePlainIconSmall.png);
+}
+
+.resource-sidebar-tree-item.resources-type-image .icon {
+    position: relative;
+    background-image: url(Images/resourcePlainIcon.png);
+    background-repeat: no-repeat;
+    content: "";
+}
+
+.resources-type-image .image-resource-icon-preview {
+    position: absolute;
+    margin: auto;
+    top: 3px;
+    bottom: 4px;
+    left: 5px;
+    right: 5px;
+    max-width: 18px;
+    max-height: 21px;
+    min-width: 1px;
+    min-height: 1px;
+}
+
+.children.small .resource-sidebar-tree-item.resources-type-image .icon {
+    background-image: url(Images/resourcePlainIconSmall.png);
+    content: "";
+}
+
+.children.small .resources-type-image .image-resource-icon-preview {
+    top: 2px;
+    bottom: 1px;
+    left: 3px;
+    right: 3px;
+    max-width: 8px;
+    max-height: 11px;
+}
+
+.resource-sidebar-tree-item.resources-type-document .icon {
+    content: url(Images/resourceDocumentIcon.png);
+}
+
+.children.small .resource-sidebar-tree-item.resources-type-document .icon {
+    content: url(Images/resourceDocumentIconSmall.png);
+}
+
+.resource-sidebar-tree-item.resources-type-stylesheet .icon {
+    content: url(Images/resourceCSSIcon.png);
+}
+
+.children.small .resource-sidebar-tree-item.resources-type-stylesheet .icon {
+    content: url(Images/resourceDocumentIconSmall.png);
+}
+
+.resource-sidebar-tree-item.resources-type-image .icon {
+    position: relative;
+    background-image: url(Images/resourcePlainIcon.png);
+    background-repeat: no-repeat;
+    content: "";
+}
+
+.children.small .resource-sidebar-tree-item.resources-type-image .icon {
+    background-image: url(Images/resourcePlainIconSmall.png);
+    content: "";
+}
+
+.resource-sidebar-tree-item.resources-type-font .icon {
+    content: url(Images/resourcePlainIcon.png);
+}
+
+.children.small .resource-sidebar-tree-item.resources-type-font .icon {
+    content: url(Images/resourcePlainIconSmall.png);
+}
+
+.resource-sidebar-tree-item.resources-type-script .icon {
+    content: url(Images/resourceJSIcon.png);
+}
+
+.children.small .resource-sidebar-tree-item.resources-type-script .icon {
+    content: url(Images/resourceDocumentIconSmall.png);
+}
+
+.resource-sidebar-tree-item.resources-type-xhr .icon {
+    content: url(Images/resourcePlainIcon.png);
+}
+
+.children.small .resource-sidebar-tree-item.resources-type-xhr .icon {
+    content: url(Images/resourceDocumentIconSmall.png);
+}
+
+.frame-storage-tree-item .icon {
+    content: url(Images/frame.png);
+}
+
+.database-storage-tree-item .icon {
+    content: url(Images/database.png);
+}
+
+.database-table-storage-tree-item .icon {
+    content: url(Images/databaseTable.png);
+}
+
+.indexed-db-storage-tree-item .icon {
+    content: url(Images/indexedDB.png);
+}
+
+.indexed-db-object-store-storage-tree-item .icon {
+    content: url(Images/indexedDBObjectStore.png);
+}
+
+.indexed-db-index-storage-tree-item .icon {
+    content: url(Images/indexedDBIndex.png);
+}
+
+.domstorage-storage-tree-item.local-storage .icon {
+    content: url(Images/localStorage.png);
+}
+
+.domstorage-storage-tree-item.session-storage .icon {
+    content: url(Images/sessionStorage.png);
+}
+
+.cookie-storage-tree-item .icon {
+    content: url(Images/cookie.png);
+}
+
+.application-cache-storage-tree-item .icon {
+    content: url(Images/applicationCache.png);
+}
+
+.file-system-storage-tree-item .icon {
+    content: url(Images/fileSystem.png);
+}
diff --git a/Source/devtools/front_end/revisionHistory.css b/Source/devtools/front_end/revisionHistory.css
new file mode 100644
index 0000000..0c77ec7
--- /dev/null
+++ b/Source/devtools/front_end/revisionHistory.css
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+.outline-disclosure.revision-history-drawer {
+    -webkit-padding-start: 0;
+    overflow: auto;
+}
+
+.outline-disclosure.revision-history-drawer ol {
+    margin-top: 2px;
+    -webkit-padding-start: 0;
+    padding-left: 0 !important;
+}
+
+.outline-disclosure.revision-history-drawer > ol {
+    padding-left: 0;
+}
+
+.outline-disclosure.revision-history-drawer li {
+    padding-left: 6px;
+    margin-top: 0;
+    margin-bottom: 0;
+    height: 13px;
+}
+
+.outline-disclosure.revision-history-drawer li.parent {
+    margin-left: 4px;
+}
+
+.revision-history-link {
+    text-decoration: underline;
+    cursor: pointer;
+    color: #00e;
+    padding: 0 4px;
+}
+
+.revision-history-link-row {
+    padding-left: 16px;
+}
+
+.outline-disclosure.revision-history-drawer .revision-history-line {
+    padding-left: 0;
+    -webkit-user-select: text;
+}
+
+.revision-history-drawer .webkit-line-number {
+    border-right: 1px solid #BBB;
+    background-color: #F0F0F0;    
+}
+
+.revision-history-drawer li.revision-history-revision {
+    padding-left: 16px;
+}
+
+.revision-history-line-added {
+    background-color: rgb(153, 238, 153);
+}
+
+.revision-history-line-removed {
+    background-color: rgb(255, 221, 221);
+}
+
+.revision-history-line-separator .webkit-line-number {
+    color: transparent;
+}
diff --git a/Source/devtools/front_end/scriptsPanel.css b/Source/devtools/front_end/scriptsPanel.css
new file mode 100644
index 0000000..cb481d6
--- /dev/null
+++ b/Source/devtools/front_end/scriptsPanel.css
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+.scripts-pause-on-exceptions-status-bar-item .glyph {
+    -webkit-mask-position: -256px 0;
+}
+
+.scripts-pause-on-exceptions-status-bar-item.toggled-all .glyph {
+    background-color: rgb(66, 129, 235);
+}
+
+.scripts-pause-on-exceptions-status-bar-item.toggled-uncaught .glyph {
+    background-color: purple;
+}
+
+.scripts-toggle-pretty-print-status-bar-item .glyph {
+    -webkit-mask-position: -256px -24px;
+}
+
+.scripts-toggle-pretty-print-status-bar-item.toggled .glyph {
+    background-color: rgb(66, 129, 235);
+}
+
+.evaluate-snippet-status-bar-item .glyph {
+    -webkit-mask-position: -64px -48px;
+}
+
+.evaluate-snippet-status-bar-item.toggled .glyph {
+    background-color: rgb(66, 129, 235);
+}
+
+#scripts-debug-toolbar {
+    position: relative;
+    margin-top: -1px;
+    height: 24px;
+    border-bottom: 1px solid rgb(202, 202, 202);
+}
+
+#scripts-editor-toolbar {
+    position: relative;
+    margin-top: -1px;
+    height: 24px;
+}
+
+#scripts-status-bar .status-bar-item img {
+    margin-top: 2px;
+}
+
+#scripts-debug-toolbar .glyph {
+    opacity: 0.8;
+}
+
+.scripts-run-snippet .glyph {
+    -webkit-mask-position: -64px -48px;
+}
+
+.scripts-pause .glyph {
+    -webkit-mask-position: -32px -72px;
+}
+
+.scripts-pause.toggled-on .glyph {
+    -webkit-mask-position: 0 -72px;
+}
+
+.scripts-step-over .glyph {
+    -webkit-mask-position: -128px -72px;
+}
+
+.scripts-step-into .glyph {
+    -webkit-mask-position: -64px -72px;
+}
+
+.scripts-step-out .glyph {
+    -webkit-mask-position: -96px -72px;
+}
+
+.scripts-toggle-breakpoints.toggled-on .glyph {
+    -webkit-mask-position: -32px 0;
+}
+
+.scripts-toggle-breakpoints .glyph {
+    -webkit-mask-position: 0 -24px;
+}
+
+#scripts-debugger-status {
+    position: absolute;
+    line-height: 24px;
+    top: 0;
+    right: 8px;
+}
+
+#scripts-debug-sidebar-resizer-widget {
+    position: absolute;
+    top: 0;
+    height: 23px;
+    right: 0;
+    width: 16px;
+    cursor: ew-resize;
+    background-image: url(Images/statusbarResizerHorizontal.png);
+    background-repeat: no-repeat;
+    background-position: center;
+    z-index: 13;
+}
+
+.scripts-navigator-resizer-widget {
+    position: absolute;
+    top: 0;
+    right: 0;
+    height: 24px;
+    width: 16px;
+    cursor: ew-resize;
+    background-image: url(Images/statusbarResizerHorizontal.png);
+    background-repeat: no-repeat;
+    background-position: center;
+}
+
+.status-bar-item.scripts-navigator-show-hide-button,
+.scripts-debugger-show-hide-button {
+    opacity: 0.7;
+}
+
+.status-bar-item.scripts-navigator-show-hide-button.toggled-hidden .glyph {
+    -webkit-mask-position: -167px -76px;
+}
+
+.status-bar-item.scripts-navigator-show-hide-button.toggled-pinned .glyph {
+    -webkit-mask-position: -199px -76px;
+}
+
+.status-bar-item.scripts-navigator-show-hide-button.toggled-overlay .glyph {
+    -webkit-mask-position: -231px -76px;
+}
+
+button.status-bar-item.scripts-navigator-show-hide-button {
+    position: absolute;
+    top: 0;
+    left: 0;
+    background-image: none;
+    height: 16px;
+    width: 16px;
+    margin: 4px 2px 2px 2px;
+    border: none;
+}
+
+button.status-bar-item.scripts-navigator-show-hide-button:active {
+    top: 1px;
+    left: 1px;
+}
+
+button.status-bar-item.scripts-navigator-show-hide-button.toggled-overlay
+{
+    left: auto;
+    right: 15px;
+}
+
+.scripts-views-container {
+    position: absolute;
+    top: 23px;
+    right: 0;
+    bottom: 0;
+    left: 0;
+}
+
+.script-view {
+    display: none;
+    overflow: hidden;
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+}
+
+.script-view.visible {
+    display: block;
+}
+
+.dedicated-worker-item {
+    margin: 5px 0px 5px 1px;
+}
+
+#shared-workers-list {
+    margin: 5px 0px 5px 20px;
+    font-style:italic;
+}
+
+#pause-workers-checkbox > input {
+    position: relative;
+    top: 2px;
+}
+
+#scripts-editor-container-tabbed-pane .tabbed-pane-header {
+    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#F5F5F5), to(#E5E5E5));
+}
+
+#scripts-editor-container-tabbed-pane .tabbed-pane-header-contents {
+    margin-left: 20px;
+    margin-right: 36px;
+}
+
+#scripts-editor-container-tabbed-pane .tabbed-pane-header-tab {
+    border-bottom: 1px solid #BBB;
+}
+
+.function-location-link {
+    float: right;
+    margin-left: 10px;
+}
+
+.function-popover-title {
+    border-bottom: 1px solid #AAA;
+    margin-bottom: 3px;
+    padding-bottom: 2px;
+}
+
+.function-popover-title .function-name {
+    font-weight: bold;
+}
+
+button.status-bar-item.scripts-debugger-show-hide-button {
+    position: absolute;
+    top: 0;
+    background-image: none;
+    height: 16px;
+    width: 16px;
+    margin: 4px 2px 2px 2px;
+    border: none;
+}
+
+.split-view-horizontal button.status-bar-item.scripts-debugger-show-hide-button,
+.split-view-horizontal #scripts-debug-sidebar-resizer-widget {
+    display: none;
+}
+
+button.status-bar-item.scripts-debugger-show-hide-button:active {
+    top: 1px;
+}
+
+button.status-bar-item.scripts-debugger-show-hide-button.toggled-shown {
+    right: 16px;
+}
+
+button.status-bar-item.scripts-debugger-show-hide-button.toggled-shown:active {
+    right: 15px;
+}
+
+.status-bar-item.scripts-debugger-show-hide-button.toggled-shown > .glyph {
+    -webkit-mask-position: -168px -76px;
+}
+
+button.status-bar-item.scripts-debugger-show-hide-button.toggled-hidden {
+    right: 1px;
+}
+
+button.status-bar-item.scripts-debugger-show-hide-button.toggled-hidden:active {
+    right: 0px;
+}
+
+.status-bar-item.scripts-debugger-show-hide-button.toggled-hidden > .glyph {
+    -webkit-mask-position: -296px -76px;
+}
+
+div.sidebar-pane-stack#scripts-debug-sidebar-contents, #scripts-sidebar-stack-pane {
+    top: 23px;
+    overflow: auto;
+}
+
+.workers-list > li {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    margin-left: 1em;
+    font-size: 12px;
+}
+
+a.worker-item {
+    color: rgb(33%, 33%, 33%);
+    cursor: pointer;
+    text-decoration: none;
+}
+
+a.worker-item:hover {
+    color: rgb(15%, 15%, 15%);
+}
+
+.source-frame-debugger-script {
+    background-color: rgba(255, 255, 194, 0.5);
+}
diff --git a/Source/devtools/front_end/sidebarPane.css b/Source/devtools/front_end/sidebarPane.css
new file mode 100644
index 0000000..1d6e8d2
--- /dev/null
+++ b/Source/devtools/front_end/sidebarPane.css
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+.sidebar-pane {
+    position: relative;
+}
+
+.sidebar-pane > .body {
+    position: relative;
+    display: none;
+    overflow-y: auto;
+    overflow-x: hidden;
+}
+
+.sidebar-pane > .body .info {
+    text-align: center;
+    font-style: italic;
+    font-size: 90%;
+    padding: 6px;
+    color: #888;
+}
+
+.sidebar-pane > .body .placard + .info {
+    border-top: 1px solid rgb(189, 189, 189);
+    background-color: rgb(255, 255, 194);
+}
+
+.sidebar-pane.visible > .body {
+    display: block;
+}
+
+.sidebar-pane .section .properties {
+    padding-left: 16px;
+}
+
+.sidebar-tabbed-pane .tabbed-pane-header {
+    background-image: -webkit-linear-gradient(rgb(243,243,243), rgb(235,235,235));
+    border-bottom: 1px solid rgb(202, 202, 202);
+}
+
+.sidebar-pane-stack > .sidebar-pane.visible:nth-last-of-type(1) {
+    border-bottom: 1px solid rgb(189, 189, 189);
+}
+
+.sidebar-pane-title {
+    position: relative;
+    background: rgb(230, 230, 230);
+    height: 20px;
+    padding: 0 5px;
+    border-top: 1px solid rgb(189, 189, 189);
+    border-bottom: 1px solid rgb(189, 189, 189);
+    line-height: 18px;
+    -webkit-background-origin: padding;
+    -webkit-background-clip: padding;
+}
+
+.sidebar-pane-title + .sidebar-pane-title, .pane:not(.visible) + .sidebar-pane-title, .sidebar-pane-title:first-of-type {
+    margin-top: -1px;
+}
+
+.sidebar-pane-title:active {
+    background-color: rgb(204, 204, 204);
+    border-top: 1px solid rgb(178, 178, 178);
+    border-bottom: 1px solid rgb(178, 178, 178);
+}
+
+.sidebar-pane-title::before {
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    opacity: 0.5;
+    float: left;
+    width: 11px;
+    height: 11px;
+    margin-right: 2px;
+    content: "a";
+    color: transparent;
+    position: relative;
+    top: 3px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.sidebar-pane-title::before {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.sidebar-pane-title::before {
+    background-position: -4px -96px;
+}
+
+.sidebar-pane-title.expanded::before {
+    background-position: -20px -96px;
+}
+
+.sidebar-pane-toolbar {
+    line-height: 18px;
+    left: 0;
+    right: 4px;
+    top: 0;
+    height: 20px;
+    position: absolute;
+    pointer-events: none;
+}
+
+.sidebar-pane-toolbar > * {
+    pointer-events: auto;
+}
+
+.sidebar-pane-toolbar > .pane-title-button {
+    float: right;
+    width: 23px;
+    height: 17px;
+    color: transparent;
+    background-color: transparent;
+    border: none;
+    background-repeat: no-repeat;
+    margin: 1px 0 0 0;
+    padding: 0;
+    -webkit-border-radius: 0;
+    -webkit-appearance: none;
+}
+
+.sidebar-pane-toolbar > .pane-title-button:hover {
+    background-position: -23px 0px;
+}
+
+.sidebar-pane-toolbar > .pane-title-button:active, .sidebar-pane-toolbar > .pane-title-button.toggled {
+    background-position: -46px 0px;
+}
+
+.sidebar-pane-toolbar > .pane-title-button.add {
+    background-image: url(Images/paneAddButtons.png);
+}
+
+.sidebar-pane-toolbar > .pane-title-button.element-state {
+    background-image: url(Images/paneElementStateButtons.png);
+}
+
+.sidebar-pane-toolbar > .pane-title-button.refresh {
+    background-image: url(Images/paneRefreshButtons.png);
+}
+
+.sidebar-pane-subtitle {
+    position: absolute;
+    right: 0;
+}
+
+body.platform-windows .sidebar-pane-subtitle {
+    padding-top: 1px;
+}
+
+.sidebar-pane-subtitle input, .section > .header input[type=checkbox] {
+    font-size: inherit;
+    height: 1em;
+    width: 1em;
+    margin-left: 0;
+    margin-top: 0;
+    margin-bottom: 0.25em;
+    vertical-align: bottom;
+}
diff --git a/Source/devtools/front_end/spectrum.css b/Source/devtools/front_end/spectrum.css
new file mode 100644
index 0000000..7a9ea28
--- /dev/null
+++ b/Source/devtools/front_end/spectrum.css
@@ -0,0 +1,114 @@
+/* https://github.com/bgrins/spectrum */
+.spectrum-container {
+    position: absolute;
+    top: 0;
+    left: 0;
+    display: inline-block;
+    background: rgba(230, 230, 230, 1) !important;
+    border: 1px solid #646464;
+    padding: 10px;
+    width: 220px;
+    z-index: 10;
+    -webkit-user-select: none;
+}
+
+.spectrum-top {
+    position: relative;
+    width: 100%;
+    display: inline-block;
+}
+
+.spectrum-color {
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 40px;
+}
+
+.spectrum-display-value {
+    -webkit-user-select: text;
+    position: relative;
+    left: 2px;
+    top: -6px;
+}
+
+.spectrum-hue {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 170px;
+    -webkit-box-reflect: right -28px;
+}
+
+.spectrum-fill {
+    /* Same as spectrum-color width to force aspect ratio. */
+    margin-top: 150px;
+}
+
+.spectrum-range-container {
+    position: relative;
+    padding-bottom: 5px;
+}
+
+.spectrum-range-container * {
+    font-size: 11px;
+    vertical-align: middle;
+}
+
+.spectrum-range-container label {
+    display: inline-block;
+    padding-right: 4px;
+}
+
+.spectrum-range-container input {
+    position: absolute;
+    left: 15px;
+    right: 40px;
+    margin: 3px 0 0 0;
+}
+
+.spectrum-dragger, .spectrum-slider {
+    -webkit-user-select: none;
+}
+
+.spectrum-sat {
+    background-image: -webkit-linear-gradient(left, white, rgba(204, 154, 129, 0));
+}
+
+.spectrum-val {
+    background-image: -webkit-linear-gradient(bottom, black, rgba(204, 154, 129, 0));
+}
+
+.spectrum-hue {
+    background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
+}
+
+.spectrum-dragger {
+    border-radius: 5px;
+    height: 5px;
+    width: 5px;
+    border: 1px solid white;
+    cursor: pointer;
+    position: absolute;
+    top: 0;
+    left: 0;
+    background: black;
+}
+
+.spectrum-slider {
+    position: absolute;
+    top: 0;
+    cursor: pointer;
+    border: 1px solid black;
+    height: 4px;
+    left: -1px;
+    right: -1px;
+}
+
+.spectrum-container .swatch {
+    width: 20px;
+    height:20px;
+    margin: 0;
+}
diff --git a/Source/devtools/front_end/splitView.css b/Source/devtools/front_end/splitView.css
new file mode 100644
index 0000000..9721f0f
--- /dev/null
+++ b/Source/devtools/front_end/splitView.css
@@ -0,0 +1,145 @@
+/*
+ * 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 GOOGLE 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 GOOGLE 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.
+ */
+
+.split-view {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    overflow: hidden;
+}
+
+.split-view-contents {
+    position: absolute;
+    overflow: auto;
+    cursor: default;
+}
+
+.split-view-contents.maximized {
+    width: 100%;
+    height: 100%;
+}
+
+.split-view-vertical > .split-view-contents {
+    top: 0;
+    bottom: 0;
+}
+
+.split-view-vertical > .split-view-contents-first {
+    left: 0;
+}
+
+.split-view-vertical > .split-view-contents-first.maximized {
+    right: 0;
+}
+
+.split-view-vertical > .split-view-contents-second {
+    right: 0;
+}
+
+.split-view-vertical > .split-view-contents-second.maximized {
+    left: 0;
+}
+
+.split-view-horizontal > .split-view-contents {
+    left: 0;
+    right: 0;
+}
+
+.split-view-horizontal > .split-view-contents-first {
+    top: 0;
+}
+
+.split-view-horizontal > .split-view-contents-first.maximized {
+    bottom: 0;
+}
+
+.split-view-horizontal > .split-view-contents-second {
+    bottom: 0;
+}
+
+.split-view-horizontal > .split-view-contents-second.maximized {
+    top: 0;
+}
+
+.split-view-vertical > .split-view-sidebar.split-view-contents-first:not(.maximized) {
+    border-right: 1px solid rgb(64%, 64%, 64%);
+}
+
+.split-view-vertical > .split-view-sidebar.split-view-contents-second:not(.maximized) {
+    border-left: 1px solid rgb(64%, 64%, 64%);
+}
+
+.split-view-horizontal > .split-view-sidebar.split-view-contents-first:not(.maximized) {
+    border-bottom: 1px solid rgb(64%, 64%, 64%);
+}
+
+.split-view-horizontal > .split-view-sidebar.split-view-contents-second:not(.maximized) {
+    border-top: 1px solid rgb(64%, 64%, 64%);
+}
+
+.split-view-vertical > .split-view-resizer {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    width: 5px;
+    z-index: 1500;
+    cursor: ew-resize;
+}
+
+.split-view-horizontal > .split-view-resizer {
+    position: absolute;
+    left: 0;
+    right: 0;
+    height: 5px;
+    z-index: 1500;
+    cursor: ns-resize;
+}
+
+.sidebar-overlay {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 10;
+    background-color: white;
+    border-right: 1px solid gray;
+    -webkit-box-shadow: rgb(90,90,90) 20px 0px 50px -25px;
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+}
+
+.sidebar-overlay-resizer {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    width: 5px;
+    z-index: 500;
+    cursor: ew-resize;
+}
diff --git a/Source/devtools/front_end/tabbedPane.css b/Source/devtools/front_end/tabbedPane.css
new file mode 100644
index 0000000..b3d5cc1
--- /dev/null
+++ b/Source/devtools/front_end/tabbedPane.css
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ * 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 GOOGLE 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 GOOGLE 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.
+ */
+ 
+ .tabbed-pane {
+    -webkit-flex-direction: column;
+    display: -webkit-flex;
+    height: 100%;
+}
+
+.tabbed-pane-content {
+    -webkit-flex: 1;
+    position: relative;
+    overflow: auto;
+}
+
+.tabbed-pane-content.has-no-tabs {
+    background-color: lightgray;
+}
+
+.tabbed-pane-header {
+    height: 23px;
+    border-bottom: 1px solid rgb(163, 163, 163);
+    overflow: hidden;
+    width: 100%;
+}
+
+.tabbed-pane-header-contents {
+    margin: 0 10px;
+}
+
+.tabbed-pane-header-tabs {
+    float: left;
+}
+
+.tabbed-pane-header-tab {
+    float: left;
+    margin-top: 2px;
+    padding: 2px 4px 2px 4px;
+    height: 21px;
+    border: 1px solid transparent;
+    border-bottom: none;
+    line-height: 15px;
+    
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.tabbed-pane-header-tab.measuring {
+    visibility: hidden;
+}
+
+.tabbed-pane-header-tab.selected {
+    background-color: white;
+    border: 1px solid rgb(163, 163, 163);
+    border-bottom: none;
+}
+
+.tabbed-pane-header-tab .close-button-gray {
+    position: relative;
+    top: 2px;
+    left: 1px;
+    margin-left: 2px;
+    margin-top: -3px;
+    visibility: hidden;
+}
+
+.tabbed-pane-header-tab:hover .close-button-gray,
+.tabbed-pane-header-tab.selected .close-button-gray {
+    visibility: visible;
+}
+
+.tabbed-pane-header-tabs-drop-down-container {
+    float: left;
+    position: relative;
+    font-size: 16px;
+    vertical-align: bottom;
+    top: 1px;
+    padding-left: 3px;
+}
+
+.tabbed-pane-header-tabs-drop-down-container.measuring {
+    visibility: hidden;
+}
+
+.tabbed-pane-header-tabs-drop-down {
+    position: relative;
+    opacity: 0.7;
+    color: rgb(30, 30, 30);
+}
+
+.tabbed-pane-header-tabs-drop-down:hover {
+    opacity: 1.0;
+}
+
+.tabbed-pane-header-tabs-drop-down:active {
+    opacity: 0.8;
+}
+
+select.tabbed-pane-header-tabs-drop-down-select {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    bottom: 0px;
+    left: 0px;
+    opacity: 0;
+    border: none;
+    margin: 0;
+    -webkit-appearance: none;
+}
diff --git a/Source/devtools/front_end/test-runner.html b/Source/devtools/front_end/test-runner.html
new file mode 100644
index 0000000..fddad5e
--- /dev/null
+++ b/Source/devtools/front_end/test-runner.html
@@ -0,0 +1,366 @@
+<html>
+<script src="jsdifflib.js"></script>
+<script src="utilities.js"></script>
+<script src="DOMExtension.js"></script>
+<script src="treeoutline.js"></script>
+
+<link rel="stylesheet" type="text/css" href="inspector.css">
+<style>
+:focus {
+    outline: none;
+}
+
+.failed {
+    color: red;
+}
+
+.timeout {
+    color: brown;
+}
+
+iframe {
+    width: 0;
+    height: 0;
+    opacity: 0;
+}
+
+</style>
+
+<script>
+
+var layoutTestsServer = "http://localhost:8002/";
+var scannerServer = "http://localhost:8002/";
+var remoteDebuggingServer = "http://localhost:9222/";
+
+var tests = [];
+var skipList = [
+    // HALT
+    "inspector/console/console-api-on-call-frame.html",
+
+    // FAILED
+    "inspector/console/console-dir-global.html",
+    "inspector/console/console-log-toString-object.html",
+    "inspector/console/console-uncaught-exception-in-eval.html",
+    "inspector/elements/edit-dom-actions.html",
+    "inspector/elements/highlight-node-scaled.html",
+    "inspector/elements/highlight-node-scroll.html",
+    "inspector/elements/highlight-node.html",
+    "inspector/elements/highlight-svg-root.html",
+    "inspector/network-status-non-http.html",
+    "inspector/storage-panel-dom-storage-update.html",
+    "inspector/styles/inject-stylesheet.html",
+    "inspector/styles/protocol-css-regions-commands.html",
+    "inspector/styles/region-style-crash.html",
+    "inspector/styles/styles-disable-then-enable-overriden-ua.html",
+    "inspector/styles/styles-url-linkify.html",
+    "inspector/styles/vendor-prefixes.html",
+    "inspector/timeline/timeline-event-dispatch.html",
+    "inspector/timeline/timeline-frames.html",
+    "inspector/timeline/timeline-network-resource.html",
+    "inspector/timeline/timeline-paint.html",
+    "inspector/timeline/timeline-receive-response-event.html",
+
+    // TIMEOUT
+    "inspector/profiler/cpu-profiler-profiling-without-inspector.html",
+    "inspector/profiler/heap-snapshot-inspect-dom-wrapper.html",
+    "inspector/timeline/timeline-network-received-data.html",
+];
+var treeOutline = null;
+
+function run(debug)
+{
+    if (window.runner && debug) {
+        window.runner.continueDebugging();
+        return;
+    }
+
+    if (window.testScannerIframe) 
+        document.body.removeChild(window.testScannerIframe);
+
+    if (window.runner)
+        window.runner.cleanup();
+
+    window.testScannerIframe = document.createElement("iframe");
+    window.testScannerIframe.src = scannerServer + "LayoutTests/http/tests/inspector/resources/test-scanner.html";
+    document.body.appendChild(window.testScannerIframe);
+    window.debug = debug;
+}
+
+function runTests()
+{
+    document.getElementById("outline").removeChildren();
+    treeOutline = new TreeOutline(document.getElementById("outline"));
+
+    document.getElementById("pass").textContent = 0;
+    document.getElementById("skip").textContent = 0;
+    document.getElementById("fail").textContent = 0;
+    document.getElementById("timeout").textContent = 0;
+    document.getElementById("remaining").textContent = tests.length;
+
+    runNextTest();
+}
+
+function interrupt()
+{
+    tests = [];
+}
+
+function runNextTest(lastResult)
+{
+    if (lastResult) {
+        var element = document.getElementById(lastResult);
+        element.textContent = parseInt(element.textContent) + 1;
+
+        element = document.getElementById("remaining");
+        element.textContent = parseInt(element.textContent) - 1;
+        if (window.debug) {
+            document.getElementById("debug").textContent = "Debug";
+            return;
+        }
+    }
+
+    var test;
+    var filter = document.getElementById("filter").value;
+    while (test = tests.shift()) {
+        if (!filter || test[0].match(filter)) {
+            new StandaloneTestRunner(layoutTestsServer + test[0], test[1], runNextTest.bind(null));
+            return;
+        }
+    }
+}
+
+function StandaloneTestRunner(testPath, expected, next)
+{
+    this._testPath = testPath;
+    this._next = next;
+    this._expected = expected;
+    this._pendingMessages = [];
+
+    this._treeElement = new TreeElement(testPath);
+    treeOutline.appendChild(this._treeElement);
+
+    for (var i = 0; !window.debug && i < skipList.length; ++i) {
+        if (testPath.indexOf(skipList[i]) !== -1) {
+            this._treeElement.title = testPath + ": SKIPPED";
+            this._next("skip");
+            return;
+        }
+    }
+    window.runner = this;
+    this._testPage = window.open("about:blank", "inspected", "width=800,height=600");
+
+    window.remoteDebuggingHandshake = this._remoteDebuggingHandshake.bind(this);
+    var script = document.createElement("script");
+    script.src = remoteDebuggingServer + "json?jsonp=remoteDebuggingHandshake";
+    document.head.appendChild(script);
+}
+
+StandaloneTestRunner.FrontendLocation = "inspector.html";
+
+StandaloneTestRunner.prototype = {
+    _remoteDebuggingHandshake: function(data)
+    {
+        for (var i = 0; i < data.length; ++i) {
+            if (data[i].url !== "about:blank")
+                continue;
+            this._debuggerURL = data[i].webSocketDebuggerUrl.replace("://", "=");
+            this._navigateTestPage();
+            break;
+        }
+    },
+
+    _navigateTestPage: function()
+    {
+        this._testPage.location.href = this._testPath;
+        var width = localStorage.getItem('inspectorWidth') || 600;
+        var height = localStorage.getItem('inspectorHeight') || 400;
+        var features = "width=" + Math.max(width , 600) + ",height=" + Math.max(height, 400);
+        this._inspectorWindowLoading = window.open(StandaloneTestRunner.FrontendLocation + "?" + this._debuggerURL, "inspector", features);
+        this._inspectorWindowLoading.dispatchStandaloneTestRunnerMessages = true;
+        
+        window.addEventListener('unload', this.cleanup.bind(this));
+
+        if (!window.debug)
+            this._watchDog = setTimeout(this._timeout.bind(this), 10000);
+    },
+
+    loadCompleted: function()
+    {
+        if (!window.debug) {
+            this._loadCompleted(this);
+            return;
+        }
+        document.getElementById("debug").textContent = "Continue";
+    },
+
+    continueDebugging: function()
+    {
+        this._loadCompleted();
+    },
+
+    _loadCompleted: function()
+    {
+        this._inspectorWindow = this._inspectorWindowLoading;
+        for (var i = 0; i < this._pendingMessages.length; ++i)
+            this._inspectorWindow.postMessage(this._pendingMessages[i], "*");
+        this._pendingMessages = [];
+    },
+
+    closeWebInspector: function()
+    {
+        if (!window.debug)
+            this._inspectorWindow.close();
+    },
+
+    notifyDone: function(actual)
+    {
+        if (this._done)
+            return;
+        this._done = true;
+
+        if (!window.debug) 
+            this.cleanup()
+
+        clearTimeout(this._watchDog);
+
+        this._treeElement.onselect = this.onTreeElementSelect.bind(this);
+
+        // TODO pavel  is the  RHS || condition wanted?
+        if (actual === this._expected || actual === this._expected + "\n") {
+            this._treeElement.title = this._testPath + ": SUCCESS";
+            this._next("pass");
+            return;
+        }
+
+        this._treeElement.title = this._testPath + ": FAILED";
+        this._treeElement.listItemElement.addStyleClass("failed");
+
+        var baseLines = difflib.stringAsLines(this._expected);
+        var newLines = difflib.stringAsLines(actual);
+        var sm = new difflib.SequenceMatcher(baseLines, newLines);
+        var opcodes = sm.get_opcodes();
+        var lastWasSeparator = false;
+
+        for (var idx = 0; idx < opcodes.length; idx++) {
+            var code = opcodes[idx];
+            var change = code[0];
+            var b = code[1];
+            var be = code[2];
+            var n = code[3];
+            var ne = code[4];
+            var rowCount = Math.max(be - b, ne - n);
+            var topRows = [];
+            var bottomRows = [];
+            for (var i = 0; i < rowCount; i++) {
+                if (change === "delete" || (change === "replace" && b < be)) {
+                    var lineNumber = b++;
+                    this._treeElement.appendChild(new TreeElement("- [" + lineNumber + "] " + baseLines[lineNumber]));
+                }
+
+                if (change === "insert" || (change === "replace" && n < ne)) {
+                    var lineNumber = n++;
+                    this._treeElement.appendChild(new TreeElement("+ [" + lineNumber + "] " + newLines[lineNumber]));
+                }
+
+                if (change === "equal") {
+                    b++;
+                    n++;
+                }
+            }
+        }
+
+        this._next("fail");
+    },
+
+    evaluateInWebInspector: function(callId, script)
+    {
+        if (this._inspectorWindow)
+            this._inspectorWindow.postMessage(["evaluateForTest", callId, script], "*");
+        else
+            this._pendingMessages.push(["evaluateForTest", callId, script]);
+    },
+
+    _timeout: function()
+    {
+        this._treeElement.title = this._testPath + ": TIMEOUT";
+        this._treeElement.listItemElement.addStyleClass("timeout");
+        this._done = true;
+        this.cleanup();
+        this._next("timeout");
+    },
+
+    cleanup: function ()
+    {
+        localStorage.setItem('inspectorWidth', this._inspectorWindowLoading.outerWidth);
+        localStorage.setItem('inspectorHeight', this._inspectorWindowLoading.outerHeight);
+        this._inspectorWindowLoading.close();
+        this._testPage.close();
+        delete window.runner;
+    },
+
+    onTreeElementSelect: function () 
+    {
+        var baseEndSentinel = '/inspector/';
+        var baseChars = this._testPath.indexOf(baseEndSentinel) + baseEndSentinel.length;
+        if (baseChars > 0) 
+            document.getElementById("filter").value = this._testPath.substr(baseChars);
+    },
+
+    display: function() { }
+}
+
+function onMessageFromTestPage(event)
+{
+    var signature = event.data;
+    var method = signature.shift();
+    if (method === "tests") {
+        tests = signature[0];
+        runTests();
+        return;
+    }
+
+    if (window.runner)
+        window.runner[method].apply(window.runner, signature);
+}
+
+function onload()
+{
+    var queryParamsObject = {};
+    var queryParams = window.location.search;
+    if (!queryParams)
+        return;
+    var params = queryParams.substring(1).split("&");
+    for (var i = 0; i < params.length; ++i) {
+        var pair = params[i].split("=");
+        queryParamsObject[pair[0]] = pair[1];
+    }
+    if ("filter" in queryParamsObject)
+        document.getElementById("filter").value = queryParamsObject["filter"];
+}
+
+window.addEventListener("message", onMessageFromTestPage, true);
+
+</script>
+<body onload="onload()">
+This is a standalone test suite for inspector front-end. Here is how you run it:
+<ul>
+<li>Check out WebKit source tree: git clone http://git.chromium.org/external/Webkit.git</li>
+<li>Run "Tools/Scripts/new-run-webkit-httpd --root=. --port=8002 --server=start"</li>
+<li>Run Chrome Canary (ToT Chromium) with following flags: "--remote-debugging-port=9222 --user-data-dir=testProfile http://localhost:8002/Source/devtools/front_end/test-runner.html"</li>
+</ul>
+
+<button onclick="run()">Run</button>
+<button id="debug" onclick="run(true)">Debug</button>
+<button onclick="interrupt()">Interrupt</button>
+Filter: <input id="filter" type="text" size="40"></input><small><i>Click on results to load filter</i></small><br>
+
+<span>Passed: <span id="pass">0</span></span>
+<span>Failed: <span id="fail">0</span></span>
+<span>Timeout: <span id="timeout">0</span></span>
+<span>Skipped: <span id="skip">0</span></span>
+<span>Remaining: <span id="remaining">0</span><br>
+
+<ol id="outline" style="font-size: 12px !important" class="source-code outline-disclosure"></ol>
+
+</body>
+</html>
diff --git a/Source/devtools/front_end/textEditor.css b/Source/devtools/front_end/textEditor.css
new file mode 100644
index 0000000..ccc7bbe
--- /dev/null
+++ b/Source/devtools/front_end/textEditor.css
@@ -0,0 +1,219 @@
+.text-editor {
+    position: absolute;
+    top:0;
+    left:0;
+    right:0;
+    bottom:0;
+    white-space: pre;
+    overflow: auto;
+}
+
+.text-editor-lines {
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    overflow: hidden;
+    -webkit-user-select: none;
+    background-color: rgb(240, 240, 240);
+    border-right: 1px solid rgb(187, 187, 187);
+    min-width: 19px;
+}
+
+.text-editor-contents {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow: auto;
+    -webkit-user-select: text;
+}
+
+.webkit-line-content > .text-editor-overlay-highlight {
+    position: absolute;
+    pointer-events: none;
+    -webkit-user-select: none;
+    z-index: 1;
+}
+
+.text-editor-token-highlight {
+    border: 1px solid gray;
+    border-radius: 3px;
+}
+
+.text-editor-brace-match {
+    border-bottom: 1px solid black;
+}
+
+.text-editor-contents .inner-container {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: auto;
+    bottom: auto;
+    min-width: 100%;
+}
+
+.text-editor-editable {
+    -webkit-user-modify: read-write-plaintext-only;
+}
+
+.text-editor-read-only {
+    -webkit-user-modify: read-only;
+    background-color: rgb(240, 240, 240);
+}
+
+.webkit-line-decorations {
+    pointer-events: none;
+    -webkit-user-select: none;
+    -webkit-user-modify: read-only;
+}
+
+.webkit-html-message-bubble {
+    -webkit-box-shadow: black 0px 2px 5px;
+    -webkit-border-radius: 9px;
+    -webkit-border-fit: lines;
+    font-size: 10px;
+    font-family: Lucida Grande, sans-serif;
+    font-weight: bold;
+    margin: 6px 25px;
+    padding: 0 7px 1px;
+    z-index:20;
+}
+
+.webkit-html-warning-message {
+    background-color: rgb(100%, 62%, 42%);
+    border: 2px solid rgb(100%, 52%, 21%);
+}
+
+.webkit-html-error-message {
+    background-color: rgb(100%, 42%, 42%);
+    border: 2px solid rgb(100%, 31%, 31%);
+}
+
+.webkit-html-message-line {
+    padding-left: 23px;
+    text-indent: -20px;
+}
+
+.webkit-html-message-line-hover {
+    padding-left: 23px;
+    text-indent: -20px;
+    white-space: auto;
+    text-overflow: auto;
+    overflow: auto;
+}
+
+.webkit-html-message-icon {
+    position: relative;
+    top: 2px;
+    margin: 0 4px;
+}
+
+.webkit-line-number {
+    color: rgb(128, 128, 128);
+    text-align: right;
+    vertical-align: top;
+    word-break: normal;
+    padding-right: 4px;
+    padding-left: 6px;
+}
+
+.webkit-line-number-outer {
+    margin-right: -4px;
+    margin-left: -4px;
+    border-color: transparent;
+    border-style: solid;
+    border-width: 0 0 0px 2px;
+    vertical-align: top;
+}
+
+.webkit-line-number-inner {
+    margin-right: 4px;
+}
+
+.webkit-breakpoint .webkit-line-number-inner, .webkit-breakpoint-conditional .webkit-line-number-inner, .webkit-execution-line .webkit-line-number-inner {
+    margin-right: -10px;
+}
+
+.webkit-breakpoint .webkit-line-number-outer {
+    color: white;
+    border-width: 0 14px 0px 2px;
+    -webkit-border-image: url(Images/breakpointBorder.png) 0 14 0 2;
+}
+
+.webkit-breakpoint-conditional .webkit-line-number-outer {
+    color: white;
+    border-width: 0 14px 0px 2px;
+    -webkit-border-image: url(Images/breakpointConditionalBorder.png) 0 14 0 2;
+}
+
+.webkit-execution-line .webkit-line-number-outer {
+    color: transparent;
+    border-width: 0 14px 0px 2px;
+    -webkit-border-image: url(Images/programCounterBorder.png) 0 14 0 2;
+}
+
+.webkit-breakpoint.webkit-execution-line .webkit-line-number-outer {
+    color: white;
+    -webkit-border-image: url(Images/breakpointCounterBorder.png) 0 14 0 2;
+}
+
+.webkit-breakpoint.webkit-execution-line .webkit-line-number-outer {
+    color: transparent;
+    -webkit-border-image: url(Images/breakpointCounterBorder.png) 0 14 0 2;
+}
+
+.webkit-breakpoint-conditional.webkit-execution-line .webkit-line-number-outer {
+    color: transparent;
+    -webkit-border-image: url(Images/breakpointConditionalCounterBorder.png) 0 14 0 2;
+}
+
+.webkit-breakpoint-disabled .webkit-line-number-outer {
+    opacity: 0.5;
+}
+
+.breakpoints-deactivated .webkit-breakpoint .webkit-line-number-outer {
+    opacity: 0.5;
+}
+
+.breakpoints-deactivated .webkit-breakpoint-disabled .webkit-line-number-outer {
+    opacity: 0.3;
+}
+
+.webkit-line-content > * {
+    position: relative;
+    z-index: 2;
+}
+
+.webkit-execution-line.webkit-line-content {
+    background-color: rgb(171, 191, 254);
+    outline: 1px solid rgb(64, 115, 244);
+}
+
+.webkit-highlighted-line.webkit-line-content {
+    -webkit-animation: "fadeout" 2s 0s;
+}
+
+@-webkit-keyframes fadeout {
+    from {background-color: rgb(255, 255, 120); }
+    to { background-color: white; }
+}
+
+.debug-fadeout {
+    -webkit-animation: "debug-fadeout" 1s 0s;
+    border: 1px solid white;
+    margin: -1px;
+}
+
+@-webkit-keyframes debug-fadeout {
+    from {
+        border-color: black;
+        background-color: rgb(255, 255, 120);
+    }
+    to {
+        background-color: white;
+        border-color: white
+    }
+}
diff --git a/Source/devtools/front_end/textPrompt.css b/Source/devtools/front_end/textPrompt.css
new file mode 100644
index 0000000..ca4ba07
--- /dev/null
+++ b/Source/devtools/front_end/textPrompt.css
@@ -0,0 +1,69 @@
+/*
+ * 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:
+ *
+ *     * 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.
+ */
+
+.suggest-box {
+    position: absolute;
+    background-color: #FFFFFF;
+    display: block;
+    border: 1px solid black;
+    padding: 2px;
+    z-index: 100;
+}
+
+.suggest-box .container {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow-x: hidden;
+    overflow-y: auto;
+}
+
+.suggest-box-content-item {
+    padding: 1px;
+    margin: 0;
+    font-size: 11px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    border: 1px solid transparent;
+}
+
+.suggest-box-content-item .prefix {
+    font-weight: bold;
+}
+
+.suggest-box-content-item.selected {
+    background-color: rgb(212, 212, 212);
+}
+
+.suggest-box-content-item:hover:not(.selected) {
+    border: 1px solid rgb(204, 204, 204);
+}
diff --git a/Source/devtools/front_end/timelinePanel.css b/Source/devtools/front_end/timelinePanel.css
new file mode 100644
index 0000000..625631b
--- /dev/null
+++ b/Source/devtools/front_end/timelinePanel.css
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Anthony Ricaud <rik@webkit.org>
+ *
+ * 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 Computer, 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.
+ */
+
+#timeline-overview-panel {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 80px;
+}
+
+#timeline-overview-panel .timeline-graph-bar {
+    pointer-events: none;
+}
+
+.timeline .sidebar {
+    overflow-y: hidden;
+    min-height: 100%;
+    bottom: auto !important;
+}
+
+.timeline.split-view-vertical .split-view-resizer {
+    top: 20px;
+}
+
+.timeline-sidebar-background {
+    top: 90px;
+    bottom: 0;
+}
+
+#timeline-overview-separator {
+    position: absolute;
+    top: 80px;
+    left: 0;
+    right: 0;
+    background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(253, 253, 253)), to(rgb(213, 213, 213)));
+    border-top: 1px solid rgb(140, 140, 140);
+    border-bottom: 1px solid rgb(115, 115, 115);
+    height: 10px;
+}
+
+#timeline-overview-sidebar {
+    position: absolute;
+    width: 200px;
+    top: 0px;
+    bottom: 0px;
+    left: 0px;
+    padding-top: 2px;
+    background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(242, 242, 242)), to(rgb(209, 209, 209)));
+    border-right: 1px solid rgb(163, 163, 163);
+}
+
+#timeline-overview-grid {
+    background-color: rgb(255, 255, 255);
+}
+
+.timeline-frame-overview #timeline-overview-grid {
+    display: none;
+}
+
+#timeline-overview-grid .resources-dividers-label-bar {
+    pointer-events: auto;
+}
+
+.timeline-frame-overview .overview-grid-window {
+    bottom: 0;
+}
+
+.timeline-frame-overview .overview-grid-dividers-background {
+    bottom: 0;
+}
+
+#timeline-overview-grid #resources-graphs {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 80px;
+}
+
+#timeline-container {
+    position: absolute;
+    top: 90px;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    border-right: 0 none transparent;
+    overflow-y: scroll;
+    overflow-x: hidden;
+}
+
+#timeline-container .split-view-sidebar {
+    z-index: 1;
+}
+
+#timeline-container .webkit-html-external-link,
+#timeline-container .webkit-html-resource-link {
+    color: inherit;
+}
+
+.timeline-misc-status-bar-items {
+    right: 64px;
+}
+
+.timeline-misc-status-bar-filters {
+    display: -webkit-flex;
+    -webkit-flex-orientation: row;
+}
+
+.timeline-category-statusbar-item {
+    height: 24px;
+    line-height: 22px;
+    padding-left: 6px;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    text-shadow: white 0 1px 0;
+    overflow: hidden;
+    min-width: 20px;
+    -webkit-flex: auto 0 1;
+}
+
+.timeline-category-statusbar-item,
+.timeline-records-counter {
+    color: rgb(65, 65, 65);
+}
+
+.timeline-category-checkbox {
+    width: 10px;
+    height: 10px;
+    margin: 0 3px 0 9px;
+    padding: 0;
+    border-radius: 2px;
+    border: solid 1px;
+    display: inline-block;
+    overflow: visible;
+    opacity: 0.8;
+    vertical-align: -1px;
+}
+
+
+.timeline-category-checkbox-check {
+    -webkit-appearance: none;
+    width: 11px;
+    height: 11px;
+    margin-top: -2px;
+    margin-left: 1px;
+}
+
+.timeline-category-checkbox-checked {
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    background-position: -129px -110px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.timeline-category-checkbox-checked {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.timeline-tree-item {
+    height: 18px;
+    line-height: 15px;
+    padding-right: 5px;
+    padding-left: 5px;
+    padding-top: 2px;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.timeline-expandable {
+    position: absolute;
+    border-left: 1px solid rgb(163, 163, 163);
+}
+
+.timeline-expandable-left {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    width: 3px;
+    border-top: 1px solid rgb(163, 163, 163);
+    border-bottom: 1px solid rgb(163, 163, 163);
+}
+
+.timeline-expandable-arrow {
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    opacity: 0.5;
+    width: 10px;
+    height: 10px;
+    position: relative;
+    top: 3px;
+    left: 2px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.timeline-expandable-arrow {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.timeline-expandable-collapsed .timeline-expandable-arrow {
+    background-position: -4px -96px;
+}
+
+.timeline-expandable-expanded .timeline-expandable-arrow {
+    background-position: -20px -96px;
+}
+
+.timeline-tree-item .type {
+    padding-left: 14px;
+}
+
+.timeline-tree-item .count {
+    font-weight: bold;
+}
+
+.timeline-tree-item .timeline-tree-icon {
+    display: block;
+    margin-top: 4px;
+    margin-left: 2px;
+    width: 7px;
+    height: 7px;
+    border-radius: 1px;
+    border: solid 1px;
+    position: absolute;
+}
+
+.timeline-tree-item.background .timeline-tree-icon {
+    background: none !important;
+}
+
+.timeline-tree-item.even {
+    background-color: rgba(0, 0, 0, 0.05);
+}
+
+.timeline-tree-item.warning::after,
+.timeline-tree-item.child-warning::after {
+    background-image: url(Images/statusbarButtonGlyphs.png);
+    background-size: 320px 120px;
+    width: 10px;
+    height: 10px;
+    float: right;
+    content: "";
+    position: relative;
+    top: 2px;
+    background-position: -202px -107px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+.timeline-tree-item.warning::after,
+.timeline-tree-item.child-warning::after {
+    background-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.timeline-tree-item.child-warning::after {
+    opacity: 0.5;
+}
+
+.timeline-tree-item .data.dimmed {
+    color: rgba(0, 0, 0, 0.7);
+}
+
+#timeline-overview-events,
+#timeline-overview-memory {
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    top: 20px;
+    z-index: 160;
+}
+
+#timeline-overview-memory  {
+    top: 25px;
+}
+
+#timeline-overview-container canvas {
+    width: 100%;
+    height: 100%;
+}
+
+#timeline-overview-frames canvas {
+    z-index: 200;
+    background-color: rgba(255, 255, 255, 0.8);
+}
+
+#timeline-graphs {
+    position: absolute;
+    left: 0;
+    right: 0;
+    max-height: 100%;
+    top: 19px;
+}
+
+.timeline-graph-side {
+    position: relative;
+    height: 18px;
+    padding: 0 5px;
+    white-space: nowrap;
+    margin-top: 0px;
+    border-top: 1px solid transparent;
+    overflow: hidden;
+    pointer-events: none;
+}
+
+.timeline-graph-bar-area {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    right: 0;
+    left: 3px;
+    pointer-events: none;
+}
+
+.timeline-graph-bar {
+    position: absolute;
+    top: -1px;
+    bottom: 0;
+    margin: auto -2px;
+    height: 10px;
+    min-width: 5px;
+    z-index: 180;
+    pointer-events: visibleFill;
+    border-radius: 1px;
+    border: 1px solid;
+}
+
+.timeline-graph-bar.with-children {
+    opacity: 0.25;
+    box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.4);
+}
+
+.timeline-graph-bar.cpu {
+    opacity: 0.7;
+}
+
+.timeline-graph-side.background .timeline-graph-bar {
+    background: transparent !important;
+    border-width: 2px;
+}
+
+.timeline-graph-side.even {
+    background-color: rgba(0, 0, 0, 0.05);
+}
+
+.timeline-aggregated-category {
+    display: inline-block;
+    height: 11px;
+    margin-right: 2px;
+    margin-left: 6px;
+    position: relative;
+    top: 2px;
+    width: 10px;
+    border: solid 1px;
+}
+
+.popover .timeline-aggregated-category.timeline-loading {
+    margin-left: 0px;
+}
+
+.garbage-collect-status-bar-item .glyph {
+    -webkit-mask-position: -128px -24px;
+}
+
+.glue-async-status-bar-item .glyph {
+    -webkit-mask-position: -128px -48px;
+}
+
+.glue-async-status-bar-item.toggled-on:disabled .glyph {
+    background-color: rgba(0, 0, 0, 0.75);
+}
+
+.timeline-frame-overview-status-bar-item.toggled-on .glyph {
+    background-color: rgb(66, 129, 235) !important;
+}
+
+.timeline-records-stats, .storage-application-cache-status, .storage-application-cache-connectivity {
+    text-shadow: white 0 1px 0;
+}
+
+.timeline-records-stats {
+    margin-top: 3px;
+    margin-left: 6px;
+    -webkit-flex: 1;
+}
+
+.timeline-records-stats-container {
+    display: inline-block;
+    border-left: 1px solid rgb(202, 202, 202);
+    height: 24px;
+    margin-left: 6px;
+}
+
+.timeline-frames-stats {
+    pointer-events: auto;
+    text-decoration: underline;
+    cursor: pointer;
+}
+
+#resources-container-content {
+    overflow: visible;
+    min-height: 100%;
+}
+
+#resources-graphs {
+    position: absolute;
+    left: 0;
+    right: 0;
+    max-height: 100%;
+    top: 112px;
+}
+
+.resources-graph-side {
+    position: relative;
+    height: 36px;
+    padding: 0 5px;
+    white-space: nowrap;
+    margin-top: 1px;
+    border-top: 1px solid transparent;
+    overflow: hidden;
+}
+
+.resources-graph-bar-area {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    right: 8px;
+    left: 9px;
+}
+
+#timeline-overview-sidebar .sidebar-tree-item {
+    line-height: 26px;
+    height: 24px;
+}
+
+#timeline-overview-sidebar .sidebar-tree-item .titles.no-subtitle {
+    top: 5px;
+}
+
+#timeline-overview-sidebar .icon {
+    height: 24px;
+    margin-top: -1px;
+    margin-left: 0;
+    margin-right: 0;
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs.png);
+    -webkit-mask-size: 320px 120px;
+    background-color: black;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+#timeline-overview-sidebar .icon {
+    -webkit-mask-image: url(Images/statusbarButtonGlyphs2x.png);
+}
+}
+
+.timeline-overview-sidebar-events .icon {
+    -webkit-mask-position: -192px -48px;
+}
+
+.timeline-overview-sidebar-frames .icon {
+    -webkit-mask-position: -160px -48px;
+}
+
+.timeline-overview-sidebar-memory .icon {
+    -webkit-mask-position: -224px -48px;
+}
+
+.memory-graph-label {
+    position: absolute;
+    left: 5px;
+    font-size: 9px;
+    color: rgb(50%, 50%, 50%);
+    white-space: nowrap;
+}
+
+.max.memory-graph-label {
+    top: 5px;
+}
+
+.min.memory-graph-label {
+    bottom: 5px;
+}
+
+#timeline-memory-splitter {
+    position: absolute;
+    z-index: 5;
+    left: 0;
+    right: 0;
+    height: 5px;
+    cursor: ns-resize;
+}
+
+#memory-counters-graph {
+    overflow: hidden;
+}
+
+#memory-graphs-container {
+    border-top: 1px solid #AAA;
+}
+
+#memory-graphs-canvas-container {
+    border-right: 1px solid #AAA;
+}
+
+#memory-graphs-canvas-container.dom-counters .resources-dividers {
+    top: 15px;
+}
+
+#memory-graphs-container .split-view-contents {
+    overflow: hidden;
+}
+
+.memory-counter-sidebar-info {
+    margin: 10px;
+}
+
+.memory-counter-sidebar-info .swatch{
+    background-image: none;
+}
+
+.memory-counter-sidebar-info.bottom-border-visible {
+    border-bottom: 1px solid #AAA;
+}
+
+.memory-counter-sidebar-info .title {
+    margin: 4px;
+}
+
+.memory-counter-value {
+    margin: 4px;
+}
+
+#counter-values-bar {
+    border-bottom: solid 1px lightgray;
+    min-height: 15px;
+}
+
+.timeline .resources-event-divider {
+    height: 19px;
+    width: 8px;
+    border-left-width: 2px;
+    border-left-style: solid;
+    bottom: auto;
+    pointer-events: auto;
+}
+
+.resources-red-divider {
+    border-color: rgba(255, 0, 0, 0.5);
+}
+
+.resources-blue-divider {
+    border-color: rgba(0, 0, 255, 0.5);
+}
+
+.resources-orange-divider {
+    border-color: rgba(255, 178, 23, 0.5);
+}
+
+.resources-divider:last-child {
+    border-color: transparent;
+}
+
+.timeline .resources-event-divider.timeline-frame-divider {
+    background-color: rgba(180, 180, 180, 0.8);
+    border-style: none;
+    width: 1px;
+    height: 100%;
+    pointer-events: none;
+}
+
+.sidebar-tree-item .timeline-frame-overview-status-bar-item {
+    position: absolute;
+    right: 10px;
+    top: 4px;
+}
+
+.timeline-frame-container {
+    height: 19px;
+    overflow: hidden;
+    background-color: rgb(220, 220, 220);
+    opacity: 0.6;
+    color: #222;
+    text-align: center;
+    padding-top: 3px;
+    z-index: 350;
+    pointer-events: auto;
+}
+
+.timeline-frame-strip {
+    position: absolute;
+    height: 19px;
+}
+
+#timeline-grid-header {
+    position: absolute;
+    left: 200px;
+    right: 0;
+    top: 90px;
+    bottom: 0;
+    pointer-events: none;
+}
+
+.timeline-cpu-bars {
+    position: absolute;
+    top: 0;
+    height: 19px;
+    z-index: 350;
+    width: 100%;
+    overflow: hidden;
+}
+
+.timeline-cpu-bars .timeline-graph-bar {
+    border-color: rgb(192, 192, 192);
+    background-color: rgba(0, 0, 0, 0.1);
+    top: 4px;
+    bottom: 4px;
+    height: auto;
+}
+
+.timeline-cpu-curtain-left, .timeline-cpu-curtain-right {
+    background-color: rgba(0, 0, 0, 0.15);
+    position: absolute;
+    top: 0;
+    height: 100%;
+}
+
+.timeline-cpu-curtain-left {
+    left: 0;
+}
+
+.timeline-cpu-curtain-right {
+    right: 0;
+}
+
+.image-preview-container {
+    background: transparent;
+    text-align: left;
+    border-spacing: 0;
+}
+
+.image-preview-container img {
+    max-width: 100px;
+    max-height: 100px;
+    background-image: url(Images/checker.png);
+    -webkit-user-select: text;
+    -webkit-user-drag: auto;
+}
+
+.image-container {
+    padding: 0;
+}
+
+.memory-category-value {
+    float: right;
+}
+
+.highlighted-timeline-record {
+    -webkit-animation: "timeline_record_highlight" 2s 0s;
+}
+
+@-webkit-keyframes timeline_record_highlight {
+    from {background-color: rgba(255, 255, 120, 1); }
+    to { background-color: rgba(255, 255, 120, 0); }
+}
diff --git a/Source/devtools/front_end/treeoutline.js b/Source/devtools/front_end/treeoutline.js
new file mode 100644
index 0000000..8f4f851
--- /dev/null
+++ b/Source/devtools/front_end/treeoutline.js
@@ -0,0 +1,891 @@
+/*
+ * Copyright (C) 2007 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+/**
+ * @constructor
+ * @param {Element} listNode
+ * @param {boolean=} nonFocusable
+ */
+function TreeOutline(listNode, nonFocusable)
+{
+    /**
+     * @type {Array.<TreeElement>}
+     */
+    this.children = [];
+    this.selectedTreeElement = null;
+    this._childrenListNode = listNode;
+    this.childrenListElement = this._childrenListNode;
+    this._childrenListNode.removeChildren();
+    this.expandTreeElementsWhenArrowing = false;
+    this.root = true;
+    this.hasChildren = false;
+    this.expanded = true;
+    this.selected = false;
+    this.treeOutline = this;
+    this.comparator = null;
+
+    this.setFocusable(!nonFocusable);
+    this._childrenListNode.addEventListener("keydown", this._treeKeyDown.bind(this), true);
+    
+    this._treeElementsMap = new Map();
+    this._expandedStateMap = new Map();
+}
+
+TreeOutline.prototype.setFocusable = function(focusable)
+{
+    if (focusable)
+        this._childrenListNode.setAttribute("tabIndex", 0);
+    else
+        this._childrenListNode.removeAttribute("tabIndex");
+}
+
+TreeOutline.prototype.appendChild = function(child)
+{
+    var insertionIndex;
+    if (this.treeOutline.comparator)
+        insertionIndex = insertionIndexForObjectInListSortedByFunction(child, this.children, this.treeOutline.comparator);
+    else
+        insertionIndex = this.children.length;
+    this.insertChild(child, insertionIndex);
+}
+
+TreeOutline.prototype.insertChild = function(child, index)
+{
+    if (!child)
+        throw("child can't be undefined or null");
+
+    var previousChild = (index > 0 ? this.children[index - 1] : null);
+    if (previousChild) {
+        previousChild.nextSibling = child;
+        child.previousSibling = previousChild;
+    } else {
+        child.previousSibling = null;
+    }
+
+    var nextChild = this.children[index];
+    if (nextChild) {
+        nextChild.previousSibling = child;
+        child.nextSibling = nextChild;
+    } else {
+        child.nextSibling = null;
+    }
+
+    this.children.splice(index, 0, child);
+    this.hasChildren = true;
+    child.parent = this;
+    child.treeOutline = this.treeOutline;
+    child.treeOutline._rememberTreeElement(child);
+
+    var current = child.children[0];
+    while (current) {
+        current.treeOutline = this.treeOutline;
+        current.treeOutline._rememberTreeElement(current);
+        current = current.traverseNextTreeElement(false, child, true);
+    }
+
+    if (child.hasChildren && typeof(child.treeOutline._expandedStateMap.get(child.representedObject)) !== "undefined")
+        child.expanded = child.treeOutline._expandedStateMap.get(child.representedObject);
+
+    if (!this._childrenListNode) {
+        this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
+        this._childrenListNode.parentTreeElement = this;
+        this._childrenListNode.classList.add("children");
+        if (this.hidden)
+            this._childrenListNode.classList.add("hidden");
+    }
+
+    child._attach();
+}
+
+TreeOutline.prototype.removeChildAtIndex = function(childIndex)
+{
+    if (childIndex < 0 || childIndex >= this.children.length)
+        throw("childIndex out of range");
+
+    var child = this.children[childIndex];
+    this.children.splice(childIndex, 1);
+
+    var parent = child.parent;
+    if (child.deselect()) {
+        if (child.previousSibling)
+            child.previousSibling.select();
+        else if (child.nextSibling)
+            child.nextSibling.select();
+        else
+            parent.select();
+    }
+
+    if (child.previousSibling)
+        child.previousSibling.nextSibling = child.nextSibling;
+    if (child.nextSibling)
+        child.nextSibling.previousSibling = child.previousSibling;
+
+    if (child.treeOutline) {
+        child.treeOutline._forgetTreeElement(child);
+        child.treeOutline._forgetChildrenRecursive(child);
+    }
+
+    child._detach();
+    child.treeOutline = null;
+    child.parent = null;
+    child.nextSibling = null;
+    child.previousSibling = null;
+}
+
+TreeOutline.prototype.removeChild = function(child)
+{
+    if (!child)
+        throw("child can't be undefined or null");
+
+    var childIndex = this.children.indexOf(child);
+    if (childIndex === -1)
+        throw("child not found in this node's children");
+
+    this.removeChildAtIndex.call(this, childIndex);
+}
+
+TreeOutline.prototype.removeChildren = function()
+{
+    for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        child.deselect();
+
+        if (child.treeOutline) {
+            child.treeOutline._forgetTreeElement(child);
+            child.treeOutline._forgetChildrenRecursive(child);
+        }
+
+        child._detach();
+        child.treeOutline = null;
+        child.parent = null;
+        child.nextSibling = null;
+        child.previousSibling = null;
+    }
+
+    this.children = [];
+}
+
+TreeOutline.prototype._rememberTreeElement = function(element)
+{
+    if (!this._treeElementsMap.get(element.representedObject))
+        this._treeElementsMap.put(element.representedObject, []);
+        
+    // check if the element is already known
+    var elements = this._treeElementsMap.get(element.representedObject);
+    if (elements.indexOf(element) !== -1)
+        return;
+
+    // add the element
+    elements.push(element);
+}
+
+TreeOutline.prototype._forgetTreeElement = function(element)
+{
+    if (this._treeElementsMap.get(element.representedObject)) {
+        var elements = this._treeElementsMap.get(element.representedObject);
+        elements.remove(element, true);
+        if (!elements.length)
+            this._treeElementsMap.remove(element.representedObject);
+    }
+}
+
+TreeOutline.prototype._forgetChildrenRecursive = function(parentElement)
+{
+    var child = parentElement.children[0];
+    while (child) {
+        this._forgetTreeElement(child);
+        child = child.traverseNextTreeElement(false, parentElement, true);
+    }
+}
+
+TreeOutline.prototype.getCachedTreeElement = function(representedObject)
+{
+    if (!representedObject)
+        return null;
+
+    var elements = this._treeElementsMap.get(representedObject);
+    if (elements && elements.length)
+        return elements[0];
+    return null;
+}
+
+TreeOutline.prototype.findTreeElement = function(representedObject, isAncestor, getParent)
+{
+    if (!representedObject)
+        return null;
+
+    var cachedElement = this.getCachedTreeElement(representedObject);
+    if (cachedElement)
+        return cachedElement;
+
+    // Walk up the parent pointers from the desired representedObject 
+    var ancestors = [];
+    for (var currentObject = getParent(representedObject); currentObject;  currentObject = getParent(currentObject)) {
+        ancestors.push(currentObject);
+        if (this.getCachedTreeElement(currentObject))  // stop climbing as soon as we hit
+            break;
+    }
+        
+    if (!currentObject)
+        return null;
+
+    // Walk down to populate each ancestor's children, to fill in the tree and the cache.
+    for (var i = ancestors.length - 1; i >= 0; --i) {
+        var treeElement = this.getCachedTreeElement(ancestors[i]);
+        if (treeElement)
+            treeElement.onpopulate();  // fill the cache with the children of treeElement
+    }
+
+    return this.getCachedTreeElement(representedObject);
+}
+
+TreeOutline.prototype.treeElementFromPoint = function(x, y)
+{
+    var node = this._childrenListNode.ownerDocument.elementFromPoint(x, y);
+    if (!node)
+        return null;
+
+    var listNode = node.enclosingNodeOrSelfWithNodeNameInArray(["ol", "li"]);
+    if (listNode)
+        return listNode.parentTreeElement || listNode.treeElement;
+    return null;
+}
+
+TreeOutline.prototype._treeKeyDown = function(event)
+{
+    if (event.target !== this._childrenListNode)
+        return;
+
+    if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey)
+        return;
+
+    var handled = false;
+    var nextSelectedElement;
+    if (event.keyIdentifier === "Up" && !event.altKey) {
+        nextSelectedElement = this.selectedTreeElement.traversePreviousTreeElement(true);
+        while (nextSelectedElement && !nextSelectedElement.selectable)
+            nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!this.expandTreeElementsWhenArrowing);
+        handled = nextSelectedElement ? true : false;
+    } else if (event.keyIdentifier === "Down" && !event.altKey) {
+        nextSelectedElement = this.selectedTreeElement.traverseNextTreeElement(true);
+        while (nextSelectedElement && !nextSelectedElement.selectable)
+            nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!this.expandTreeElementsWhenArrowing);
+        handled = nextSelectedElement ? true : false;
+    } else if (event.keyIdentifier === "Left") {
+        if (this.selectedTreeElement.expanded) {
+            if (event.altKey)
+                this.selectedTreeElement.collapseRecursively();
+            else
+                this.selectedTreeElement.collapse();
+            handled = true;
+        } else if (this.selectedTreeElement.parent && !this.selectedTreeElement.parent.root) {
+            handled = true;
+            if (this.selectedTreeElement.parent.selectable) {
+                nextSelectedElement = this.selectedTreeElement.parent;
+                while (nextSelectedElement && !nextSelectedElement.selectable)
+                    nextSelectedElement = nextSelectedElement.parent;
+                handled = nextSelectedElement ? true : false;
+            } else if (this.selectedTreeElement.parent)
+                this.selectedTreeElement.parent.collapse();
+        }
+    } else if (event.keyIdentifier === "Right") {
+        if (!this.selectedTreeElement.revealed()) {
+            this.selectedTreeElement.reveal();
+            handled = true;
+        } else if (this.selectedTreeElement.hasChildren) {
+            handled = true;
+            if (this.selectedTreeElement.expanded) {
+                nextSelectedElement = this.selectedTreeElement.children[0];
+                while (nextSelectedElement && !nextSelectedElement.selectable)
+                    nextSelectedElement = nextSelectedElement.nextSibling;
+                handled = nextSelectedElement ? true : false;
+            } else {
+                if (event.altKey)
+                    this.selectedTreeElement.expandRecursively();
+                else
+                    this.selectedTreeElement.expand();
+            }
+        }
+    } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 /* Delete */)
+        handled = this.selectedTreeElement.ondelete();
+    else if (isEnterKey(event))
+        handled = this.selectedTreeElement.onenter();
+    else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Space.code)
+        handled = this.selectedTreeElement.onspace();
+
+    if (nextSelectedElement) {
+        nextSelectedElement.reveal();
+        nextSelectedElement.select(false, true);
+    }
+
+    if (handled)
+        event.consume(true);
+}
+
+TreeOutline.prototype.expand = function()
+{
+    // this is the root, do nothing
+}
+
+TreeOutline.prototype.collapse = function()
+{
+    // this is the root, do nothing
+}
+
+TreeOutline.prototype.revealed = function()
+{
+    return true;
+}
+
+TreeOutline.prototype.reveal = function()
+{
+    // this is the root, do nothing
+}
+
+TreeOutline.prototype.select = function()
+{
+    // this is the root, do nothing
+}
+
+/**
+ * @param {boolean=} omitFocus
+ */
+TreeOutline.prototype.revealAndSelect = function(omitFocus)
+{
+    // this is the root, do nothing
+}
+
+/**
+ * @constructor
+ * @param {Object=} representedObject
+ * @param {boolean=} hasChildren
+ */
+function TreeElement(title, representedObject, hasChildren)
+{
+    this._title = title;
+    this.representedObject = (representedObject || {});
+
+    this._hidden = false;
+    this._selectable = true;
+    this.expanded = false;
+    this.selected = false;
+    this.hasChildren = hasChildren;
+    this.children = [];
+    this.treeOutline = null;
+    this.parent = null;
+    this.previousSibling = null;
+    this.nextSibling = null;
+    this._listItemNode = null;
+}
+
+TreeElement.prototype = {
+    arrowToggleWidth: 10,
+
+    get selectable() {
+        if (this._hidden)
+            return false;
+        return this._selectable;
+    },
+
+    set selectable(x) {
+        this._selectable = x;
+    },
+
+    get listItemElement() {
+        return this._listItemNode;
+    },
+
+    get childrenListElement() {
+        return this._childrenListNode;
+    },
+
+    get title() {
+        return this._title;
+    },
+
+    set title(x) {
+        this._title = x;
+        this._setListItemNodeContent();
+    },
+
+    get tooltip() {
+        return this._tooltip;
+    },
+
+    set tooltip(x) {
+        this._tooltip = x;
+        if (this._listItemNode)
+            this._listItemNode.title = x ? x : "";
+    },
+
+    get hasChildren() {
+        return this._hasChildren;
+    },
+
+    set hasChildren(x) {
+        if (this._hasChildren === x)
+            return;
+
+        this._hasChildren = x;
+
+        if (!this._listItemNode)
+            return;
+
+        if (x)
+            this._listItemNode.classList.add("parent");
+        else {
+            this._listItemNode.classList.remove("parent");
+            this.collapse();
+        }
+    },
+
+    get hidden() {
+        return this._hidden;
+    },
+
+    set hidden(x) {
+        if (this._hidden === x)
+            return;
+
+        this._hidden = x;
+
+        if (x) {
+            if (this._listItemNode)
+                this._listItemNode.classList.add("hidden");
+            if (this._childrenListNode)
+                this._childrenListNode.classList.add("hidden");
+        } else {
+            if (this._listItemNode)
+                this._listItemNode.classList.remove("hidden");
+            if (this._childrenListNode)
+                this._childrenListNode.classList.remove("hidden");
+        }
+    },
+
+    get shouldRefreshChildren() {
+        return this._shouldRefreshChildren;
+    },
+
+    set shouldRefreshChildren(x) {
+        this._shouldRefreshChildren = x;
+        if (x && this.expanded)
+            this.expand();
+    },
+
+    _setListItemNodeContent: function()
+    {
+        if (!this._listItemNode)
+            return;
+
+        if (typeof this._title === "string")
+            this._listItemNode.textContent = this._title;
+        else {
+            this._listItemNode.removeChildren();
+            if (this._title)
+                this._listItemNode.appendChild(this._title);
+        }
+    }
+}
+
+TreeElement.prototype.appendChild = TreeOutline.prototype.appendChild;
+TreeElement.prototype.insertChild = TreeOutline.prototype.insertChild;
+TreeElement.prototype.removeChild = TreeOutline.prototype.removeChild;
+TreeElement.prototype.removeChildAtIndex = TreeOutline.prototype.removeChildAtIndex;
+TreeElement.prototype.removeChildren = TreeOutline.prototype.removeChildren;
+
+TreeElement.prototype._attach = function()
+{
+    if (!this._listItemNode || this.parent._shouldRefreshChildren) {
+        if (this._listItemNode && this._listItemNode.parentNode)
+            this._listItemNode.parentNode.removeChild(this._listItemNode);
+
+        this._listItemNode = this.treeOutline._childrenListNode.ownerDocument.createElement("li");
+        this._listItemNode.treeElement = this;
+        this._setListItemNodeContent();
+        this._listItemNode.title = this._tooltip ? this._tooltip : "";
+
+        if (this.hidden)
+            this._listItemNode.classList.add("hidden");
+        if (this.hasChildren)
+            this._listItemNode.classList.add("parent");
+        if (this.expanded)
+            this._listItemNode.classList.add("expanded");
+        if (this.selected)
+            this._listItemNode.classList.add("selected");
+
+        this._listItemNode.addEventListener("mousedown", TreeElement.treeElementMouseDown, false);
+        this._listItemNode.addEventListener("click", TreeElement.treeElementToggled, false);
+        this._listItemNode.addEventListener("dblclick", TreeElement.treeElementDoubleClicked, false);
+
+        this.onattach();
+    }
+
+    var nextSibling = null;
+    if (this.nextSibling && this.nextSibling._listItemNode && this.nextSibling._listItemNode.parentNode === this.parent._childrenListNode)
+        nextSibling = this.nextSibling._listItemNode;
+    this.parent._childrenListNode.insertBefore(this._listItemNode, nextSibling);
+    if (this._childrenListNode)
+        this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
+    if (this.selected)
+        this.select();
+    if (this.expanded)
+        this.expand();
+}
+
+TreeElement.prototype._detach = function()
+{
+    if (this._listItemNode && this._listItemNode.parentNode)
+        this._listItemNode.parentNode.removeChild(this._listItemNode);
+    if (this._childrenListNode && this._childrenListNode.parentNode)
+        this._childrenListNode.parentNode.removeChild(this._childrenListNode);
+}
+
+TreeElement.treeElementMouseDown = function(event)
+{
+    var element = event.currentTarget;
+    if (!element || !element.treeElement || !element.treeElement.selectable)
+        return;
+
+    if (element.treeElement.isEventWithinDisclosureTriangle(event))
+        return;
+
+    element.treeElement.selectOnMouseDown(event);
+}
+
+TreeElement.treeElementToggled = function(event)
+{
+    var element = event.currentTarget;
+    if (!element || !element.treeElement)
+        return;
+
+    var toggleOnClick = element.treeElement.toggleOnClick && !element.treeElement.selectable;
+    var isInTriangle = element.treeElement.isEventWithinDisclosureTriangle(event);
+    if (!toggleOnClick && !isInTriangle)
+        return;
+
+    if (element.treeElement.expanded) {
+        if (event.altKey)
+            element.treeElement.collapseRecursively();
+        else
+            element.treeElement.collapse();
+    } else {
+        if (event.altKey)
+            element.treeElement.expandRecursively();
+        else
+            element.treeElement.expand();
+    }
+    event.consume();
+}
+
+TreeElement.treeElementDoubleClicked = function(event)
+{
+    var element = event.currentTarget;
+    if (!element || !element.treeElement)
+        return;
+
+    var handled = element.treeElement.ondblclick.call(element.treeElement, event);
+    if (handled)
+        return;
+    if (element.treeElement.hasChildren && !element.treeElement.expanded)
+        element.treeElement.expand();
+}
+
+TreeElement.prototype.collapse = function()
+{
+    if (this._listItemNode)
+        this._listItemNode.classList.remove("expanded");
+    if (this._childrenListNode)
+        this._childrenListNode.classList.remove("expanded");
+
+    this.expanded = false;
+    
+    if (this.treeOutline)
+        this.treeOutline._expandedStateMap.put(this.representedObject, false);
+
+    this.oncollapse();
+}
+
+TreeElement.prototype.collapseRecursively = function()
+{
+    var item = this;
+    while (item) {
+        if (item.expanded)
+            item.collapse();
+        item = item.traverseNextTreeElement(false, this, true);
+    }
+}
+
+TreeElement.prototype.expand = function()
+{
+    if (!this.hasChildren || (this.expanded && !this._shouldRefreshChildren && this._childrenListNode))
+        return;
+
+    // Set this before onpopulate. Since onpopulate can add elements, this makes
+    // sure the expanded flag is true before calling those functions. This prevents the possibility
+    // of an infinite loop if onpopulate were to call expand.
+
+    this.expanded = true;
+    if (this.treeOutline)
+        this.treeOutline._expandedStateMap.put(this.representedObject, true);
+
+    if (this.treeOutline && (!this._childrenListNode || this._shouldRefreshChildren)) {
+        if (this._childrenListNode && this._childrenListNode.parentNode)
+            this._childrenListNode.parentNode.removeChild(this._childrenListNode);
+
+        this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
+        this._childrenListNode.parentTreeElement = this;
+        this._childrenListNode.classList.add("children");
+
+        if (this.hidden)
+            this._childrenListNode.classList.add("hidden");
+
+        this.onpopulate();
+
+        for (var i = 0; i < this.children.length; ++i)
+            this.children[i]._attach();
+
+        delete this._shouldRefreshChildren;
+    }
+
+    if (this._listItemNode) {
+        this._listItemNode.classList.add("expanded");
+        if (this._childrenListNode && this._childrenListNode.parentNode != this._listItemNode.parentNode)
+            this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
+    }
+
+    if (this._childrenListNode)
+        this._childrenListNode.classList.add("expanded");
+
+    this.onexpand();
+}
+
+TreeElement.prototype.expandRecursively = function(maxDepth)
+{
+    var item = this;
+    var info = {};
+    var depth = 0;
+
+    // The Inspector uses TreeOutlines to represents object properties, so recursive expansion
+    // in some case can be infinite, since JavaScript objects can hold circular references.
+    // So default to a recursion cap of 3 levels, since that gives fairly good results.
+    if (isNaN(maxDepth))
+        maxDepth = 3;
+
+    while (item) {
+        if (depth < maxDepth)
+            item.expand();
+        item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info);
+        depth += info.depthChange;
+    }
+}
+
+TreeElement.prototype.hasAncestor = function(ancestor) {
+    if (!ancestor)
+        return false;
+
+    var currentNode = this.parent;
+    while (currentNode) {
+        if (ancestor === currentNode)
+            return true;
+        currentNode = currentNode.parent;
+    }
+
+    return false;
+}
+
+TreeElement.prototype.reveal = function()
+{
+    var currentAncestor = this.parent;
+    while (currentAncestor && !currentAncestor.root) {
+        if (!currentAncestor.expanded)
+            currentAncestor.expand();
+        currentAncestor = currentAncestor.parent;
+    }
+
+    this.onreveal(this);
+}
+
+TreeElement.prototype.revealed = function()
+{
+    var currentAncestor = this.parent;
+    while (currentAncestor && !currentAncestor.root) {
+        if (!currentAncestor.expanded)
+            return false;
+        currentAncestor = currentAncestor.parent;
+    }
+
+    return true;
+}
+
+TreeElement.prototype.selectOnMouseDown = function(event)
+{
+    if (this.select(false, true))
+        event.consume(true);
+}
+
+/**
+ * @param {boolean=} omitFocus
+ * @param {boolean=} selectedByUser
+ * @return {boolean}
+ */
+TreeElement.prototype.select = function(omitFocus, selectedByUser)
+{
+    if (!this.treeOutline || !this.selectable || this.selected)
+        return false;
+
+    if (this.treeOutline.selectedTreeElement)
+        this.treeOutline.selectedTreeElement.deselect();
+
+    this.selected = true;
+
+    if(!omitFocus)
+        this.treeOutline._childrenListNode.focus();
+
+    // Focusing on another node may detach "this" from tree.
+    if (!this.treeOutline)
+        return false;
+    this.treeOutline.selectedTreeElement = this;
+    if (this._listItemNode)
+        this._listItemNode.classList.add("selected");
+
+    return this.onselect(selectedByUser);
+}
+
+/**
+ * @param {boolean=} omitFocus
+ */
+TreeElement.prototype.revealAndSelect = function(omitFocus)
+{
+    this.reveal();
+    this.select(omitFocus);
+}
+
+/**
+ * @param {boolean=} supressOnDeselect
+ */
+TreeElement.prototype.deselect = function(supressOnDeselect)
+{
+    if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !this.selected)
+        return false;
+
+    this.selected = false;
+    this.treeOutline.selectedTreeElement = null;
+    if (this._listItemNode)
+        this._listItemNode.classList.remove("selected");
+    return true;
+}
+
+// Overridden by subclasses.
+TreeElement.prototype.onpopulate = function() { }
+TreeElement.prototype.onenter = function() { }
+TreeElement.prototype.ondelete = function() { }
+TreeElement.prototype.onspace = function() { }
+TreeElement.prototype.onattach = function() { }
+TreeElement.prototype.onexpand = function() { }
+TreeElement.prototype.oncollapse = function() { }
+TreeElement.prototype.ondblclick = function() { }
+TreeElement.prototype.onreveal = function() { }
+/** @param {boolean=} selectedByUser */
+TreeElement.prototype.onselect = function(selectedByUser) { }
+
+/**
+ * @param {boolean} skipUnrevealed
+ * @param {(TreeOutline|TreeElement)=} stayWithin
+ * @param {boolean=} dontPopulate
+ * @param {Object=} info
+ * @return {TreeElement}
+ */
+TreeElement.prototype.traverseNextTreeElement = function(skipUnrevealed, stayWithin, dontPopulate, info)
+{
+    if (!dontPopulate && this.hasChildren)
+        this.onpopulate();
+
+    if (info)
+        info.depthChange = 0;
+
+    var element = skipUnrevealed ? (this.revealed() ? this.children[0] : null) : this.children[0];
+    if (element && (!skipUnrevealed || (skipUnrevealed && this.expanded))) {
+        if (info)
+            info.depthChange = 1;
+        return element;
+    }
+
+    if (this === stayWithin)
+        return null;
+
+    element = skipUnrevealed ? (this.revealed() ? this.nextSibling : null) : this.nextSibling;
+    if (element)
+        return element;
+
+    element = this;
+    while (element && !element.root && !(skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) && element.parent !== stayWithin) {
+        if (info)
+            info.depthChange -= 1;
+        element = element.parent;
+    }
+
+    if (!element)
+        return null;
+
+    return (skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling);
+}
+
+/**
+ * @param {boolean} skipUnrevealed
+ * @param {boolean=} dontPopulate
+ * @return {TreeElement}
+ */
+TreeElement.prototype.traversePreviousTreeElement = function(skipUnrevealed, dontPopulate)
+{
+    var element = skipUnrevealed ? (this.revealed() ? this.previousSibling : null) : this.previousSibling;
+    if (!dontPopulate && element && element.hasChildren)
+        element.onpopulate();
+
+    while (element && (skipUnrevealed ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1])) {
+        if (!dontPopulate && element.hasChildren)
+            element.onpopulate();
+        element = (skipUnrevealed ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1]);
+    }
+
+    if (element)
+        return element;
+
+    if (!this.parent || this.parent.root)
+        return null;
+
+    return this.parent;
+}
+
+TreeElement.prototype.isEventWithinDisclosureTriangle = function(event)
+{
+    // FIXME: We should not use getComputedStyle(). For that we need to get rid of using ::before for disclosure triangle. (http://webk.it/74446) 
+    var paddingLeftValue = window.getComputedStyle(this._listItemNode).getPropertyCSSValue("padding-left");
+    var computedLeftPadding = paddingLeftValue ? paddingLeftValue.getFloatValue(CSSPrimitiveValue.CSS_PX) : 0;
+    var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding;
+    return event.pageX >= left && event.pageX <= left + this.arrowToggleWidth && this.hasChildren;
+}
diff --git a/Source/devtools/front_end/utilities.js b/Source/devtools/front_end/utilities.js
new file mode 100644
index 0000000..2534cb5
--- /dev/null
+++ b/Source/devtools/front_end/utilities.js
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2007 Apple Inc.  All rights reserved.
+ * Copyright (C) 2012 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.
+ * 3.  Neither the name of Apple Computer, 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.
+ */
+
+Object.isEmpty = function(obj)
+{
+    for (var i in obj)
+        return false;
+    return true;
+}
+
+Object.values = function(obj)
+{
+    var keys = Object.keys(obj);
+    var result = [];
+
+    for (var i = 0; i < keys.length; ++i)
+        result.push(obj[keys[i]]);
+    return result;
+}
+
+String.prototype.hasSubstring = function(string, caseInsensitive)
+{
+    if (!caseInsensitive)
+        return this.indexOf(string) !== -1;
+    return this.match(new RegExp(string.escapeForRegExp(), "i"));
+}
+
+String.prototype.findAll = function(string)
+{
+    var matches = [];
+    var i = this.indexOf(string);
+    while (i !== -1) {
+        matches.push(i);
+        i = this.indexOf(string, i + string.length);
+    }
+    return matches;
+}
+
+String.prototype.lineEndings = function()
+{
+    if (!this._lineEndings) {
+        this._lineEndings = this.findAll("\n");
+        this._lineEndings.push(this.length);
+    }
+    return this._lineEndings;
+}
+
+String.prototype.escapeCharacters = function(chars)
+{
+    var foundChar = false;
+    for (var i = 0; i < chars.length; ++i) {
+        if (this.indexOf(chars.charAt(i)) !== -1) {
+            foundChar = true;
+            break;
+        }
+    }
+
+    if (!foundChar)
+        return String(this);
+
+    var result = "";
+    for (var i = 0; i < this.length; ++i) {
+        if (chars.indexOf(this.charAt(i)) !== -1)
+            result += "\\";
+        result += this.charAt(i);
+    }
+
+    return result;
+}
+
+String.regexSpecialCharacters = function()
+{
+    return "^[]{}()\\.$*+?|-,";
+}
+
+String.prototype.escapeForRegExp = function()
+{
+    return this.escapeCharacters(String.regexSpecialCharacters);
+}
+
+String.prototype.escapeHTML = function()
+{
+    return this.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;"); //" doublequotes just for editor
+}
+
+String.prototype.collapseWhitespace = function()
+{
+    return this.replace(/[\s\xA0]+/g, " ");
+}
+
+String.prototype.trimMiddle = function(maxLength)
+{
+    if (this.length <= maxLength)
+        return String(this);
+    var leftHalf = maxLength >> 1;
+    var rightHalf = maxLength - leftHalf - 1;
+    return this.substr(0, leftHalf) + "\u2026" + this.substr(this.length - rightHalf, rightHalf);
+}
+
+String.prototype.trimEnd = function(maxLength)
+{
+    if (this.length <= maxLength)
+        return String(this);
+    return this.substr(0, maxLength - 1) + "\u2026";
+}
+
+String.prototype.trimURL = function(baseURLDomain)
+{
+    var result = this.replace(/^(https|http|file):\/\//i, "");
+    if (baseURLDomain)
+        result = result.replace(new RegExp("^" + baseURLDomain.escapeForRegExp(), "i"), "");
+    return result;
+}
+
+String.prototype.toTitleCase = function()
+{
+    return this.substring(0, 1).toUpperCase() + this.substring(1);
+}
+
+/**
+ * @param {string} other
+ * @return {number}
+ */
+String.prototype.compareTo = function(other)
+{
+    if (this > other)
+        return 1;
+    if (this < other)
+        return -1;
+    return 0;
+}
+
+/**
+ * @param {string} href
+ * @return {string}
+ */
+function sanitizeHref(href)
+{
+    return href && href.trim().toLowerCase().startsWith("javascript:") ? "" : href;
+}
+
+String.prototype.removeURLFragment = function()
+{
+    var fragmentIndex = this.indexOf("#");
+    if (fragmentIndex == -1)
+        fragmentIndex = this.length;
+    return this.substring(0, fragmentIndex);
+}
+
+String.prototype.startsWith = function(substring)
+{
+    return !this.lastIndexOf(substring, 0);
+}
+
+String.prototype.endsWith = function(substring)
+{
+    return this.indexOf(substring, this.length - substring.length) !== -1;
+}
+
+Number.constrain = function(num, min, max)
+{
+    if (num < min)
+        num = min;
+    else if (num > max)
+        num = max;
+    return num;
+}
+
+Date.prototype.toISO8601Compact = function()
+{
+    function leadZero(x)
+    {
+        return x > 9 ? '' + x : '0' + x
+    }
+    return this.getFullYear() +
+           leadZero(this.getMonth() + 1) +
+           leadZero(this.getDate()) + 'T' +
+           leadZero(this.getHours()) +
+           leadZero(this.getMinutes()) +
+           leadZero(this.getSeconds());
+}
+
+Object.defineProperty(Array.prototype, "remove",
+{
+    /**
+     * @this {Array.<*>}
+     */
+    value: function(value, onlyFirst)
+    {
+        if (onlyFirst) {
+            var index = this.indexOf(value);
+            if (index !== -1)
+                this.splice(index, 1);
+            return;
+        }
+
+        var length = this.length;
+        for (var i = 0; i < length; ++i) {
+            if (this[i] === value)
+                this.splice(i, 1);
+        }
+    }
+});
+
+Object.defineProperty(Array.prototype, "keySet",
+{
+    /**
+     * @this {Array.<*>}
+     */
+    value: function()
+    {
+        var keys = {};
+        for (var i = 0; i < this.length; ++i)
+            keys[this[i]] = true;
+        return keys;
+    }
+});
+
+Object.defineProperty(Array.prototype, "upperBound",
+{
+    /**
+     * @this {Array.<number>}
+     */
+    value: function(value)
+    {
+        var first = 0;
+        var count = this.length;
+        while (count > 0) {
+          var step = count >> 1;
+          var middle = first + step;
+          if (value >= this[middle]) {
+              first = middle + 1;
+              count -= step + 1;
+          } else
+              count = step;
+        }
+        return first;
+    }
+});
+
+Object.defineProperty(Array.prototype, "rotate",
+{
+    /**
+     * @this {Array.<*>}
+     * @param {number} index
+     * @return {Array.<*>}
+     */
+    value: function(index)
+    {
+        var result = [];
+        for (var i = index; i < index + this.length; ++i)
+            result.push(this[i % this.length]);
+        return result;
+    }
+});
+
+Object.defineProperty(Uint32Array.prototype, "sort", {
+   value: Array.prototype.sort
+});
+
+(function() {
+var partition = {
+    /**
+     * @this {Array.<number>}
+     * @param {function(number,number):number} comparator
+     * @param {number} left
+     * @param {number} right
+     * @param {number} pivotIndex
+     */
+    value: function(comparator, left, right, pivotIndex)
+    {
+        function swap(array, i1, i2)
+        {
+            var temp = array[i1];
+            array[i1] = array[i2];
+            array[i2] = temp;
+        }
+
+        var pivotValue = this[pivotIndex];
+        swap(this, right, pivotIndex);
+        var storeIndex = left;
+        for (var i = left; i < right; ++i) {
+            if (comparator(this[i], pivotValue) < 0) {
+                swap(this, storeIndex, i);
+                ++storeIndex;
+            }
+        }
+        swap(this, right, storeIndex);
+        return storeIndex;
+    }
+};
+Object.defineProperty(Array.prototype, "partition", partition);
+Object.defineProperty(Uint32Array.prototype, "partition", partition);
+
+var sortRange = {
+    /**
+     * @this {Array.<number>}
+     * @param {function(number,number):number} comparator
+     * @param {number} leftBound
+     * @param {number} rightBound
+     * @param {number} k
+     */
+    value: function(comparator, leftBound, rightBound, k)
+    {
+        function quickSortFirstK(array, comparator, left, right, k)
+        {
+            if (right <= left)
+                return;
+            var pivotIndex = Math.floor(Math.random() * (right - left)) + left;
+            var pivotNewIndex = array.partition(comparator, left, right, pivotIndex);
+            quickSortFirstK(array, comparator, left, pivotNewIndex - 1, k);
+            if (pivotNewIndex < left + k - 1)
+                quickSortFirstK(array, comparator, pivotNewIndex + 1, right, left + k - 1 - pivotNewIndex);
+        }
+
+        if (leftBound === 0 && rightBound === (this.length - 1) && k >= this.length)
+            this.sort(comparator);
+        else
+            quickSortFirstK(this, comparator, leftBound, rightBound, k);
+        return this;
+    }
+}
+Object.defineProperty(Array.prototype, "sortRange", sortRange);
+Object.defineProperty(Uint32Array.prototype, "sortRange", sortRange);
+})();
+
+Object.defineProperty(Array.prototype, "qselect",
+{
+    /**
+     * @this {Array.<number>}
+     * @param {number} k
+     * @param {function(number,number):boolean=} comparator
+     */
+    value: function(k, comparator)
+    {
+        if (k < 0 || k >= this.length)
+            return;
+        if (!comparator)
+            comparator = function(a, b) { return a - b; }
+
+        var low = 0;
+        var high = this.length - 1;
+        for (;;) {
+            var pivotPosition = this.partition(comparator, low, high, Math.floor((high + low) / 2));
+            if (pivotPosition === k)
+                return this[k];
+            else if (pivotPosition > k)
+                high = pivotPosition - 1;
+            else
+                low = pivotPosition + 1;
+        }
+    }
+});
+
+/**
+ * @param {*} object
+ * @param {Array.<*>} array
+ * @param {function(*, *):number} comparator
+ */
+function binarySearch(object, array, comparator)
+{
+    var first = 0;
+    var last = array.length - 1;
+
+    while (first <= last) {
+        var mid = (first + last) >> 1;
+        var c = comparator(object, array[mid]);
+        if (c > 0)
+            first = mid + 1;
+        else if (c < 0)
+            last = mid - 1;
+        else
+            return mid;
+    }
+
+    // Return the nearest lesser index, "-1" means "0, "-2" means "1", etc.
+    return -(first + 1);
+}
+
+Object.defineProperty(Array.prototype, "binaryIndexOf",
+{
+    /**
+     * @this {Array.<*>}
+     * @param {function(*, *):number} comparator
+     */
+    value: function(value, comparator)
+    {
+        var result = binarySearch(value, this, comparator);
+        return result >= 0 ? result : -1;
+    }
+});
+
+Object.defineProperty(Array.prototype, "select",
+{
+    /**
+     * @this {Array.<*>}
+     * @param {string} field
+     * @return {Array.<*>}
+     */
+    value: function(field)
+    {
+        var result = new Array(this.length);
+        for (var i = 0; i < this.length; ++i)
+            result[i] = this[i][field];
+        return result;
+    }
+});
+
+Object.defineProperty(Array.prototype, "peekLast",
+{
+    /**
+     * @this {Array.<*>}
+     * @return {*}
+     */
+    value: function()
+    {
+        return this[this.length - 1];
+    }
+});
+
+/**
+ * @param {*} anObject
+ * @param {Array.<*>} aList
+ * @param {function(*, *)} aFunction
+ */
+function insertionIndexForObjectInListSortedByFunction(anObject, aList, aFunction)
+{
+    var index = binarySearch(anObject, aList, aFunction);
+    if (index < 0)
+        // See binarySearch implementation.
+        return -index - 1;
+    else {
+        // Return the first occurance of an item in the list.
+        while (index > 0 && aFunction(anObject, aList[index - 1]) === 0)
+            index--;
+        return index;
+    }
+}
+
+/**
+ * @param {string} format
+ * @param {...*} var_arg
+ */
+String.sprintf = function(format, var_arg)
+{
+    return String.vsprintf(format, Array.prototype.slice.call(arguments, 1));
+}
+
+String.tokenizeFormatString = function(format, formatters)
+{
+    var tokens = [];
+    var substitutionIndex = 0;
+
+    function addStringToken(str)
+    {
+        tokens.push({ type: "string", value: str });
+    }
+
+    function addSpecifierToken(specifier, precision, substitutionIndex)
+    {
+        tokens.push({ type: "specifier", specifier: specifier, precision: precision, substitutionIndex: substitutionIndex });
+    }
+
+    function isDigit(c)
+    {
+        return !!/[0-9]/.exec(c);
+    }
+
+    var index = 0;
+    for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; precentIndex = format.indexOf("%", index)) {
+        addStringToken(format.substring(index, precentIndex));
+        index = precentIndex + 1;
+
+        if (isDigit(format[index])) {
+            // The first character is a number, it might be a substitution index.
+            var number = parseInt(format.substring(index), 10);
+            while (isDigit(format[index]))
+                ++index;
+
+            // If the number is greater than zero and ends with a "$",
+            // then this is a substitution index.
+            if (number > 0 && format[index] === "$") {
+                substitutionIndex = (number - 1);
+                ++index;
+            }
+        }
+
+        var precision = -1;
+        if (format[index] === ".") {
+            // This is a precision specifier. If no digit follows the ".",
+            // then the precision should be zero.
+            ++index;
+            precision = parseInt(format.substring(index), 10);
+            if (isNaN(precision))
+                precision = 0;
+
+            while (isDigit(format[index]))
+                ++index;
+        }
+
+        if (!(format[index] in formatters)) {
+            addStringToken(format.substring(precentIndex, index + 1));
+            ++index;
+            continue;
+        }
+
+        addSpecifierToken(format[index], precision, substitutionIndex);
+
+        ++substitutionIndex;
+        ++index;
+    }
+
+    addStringToken(format.substring(index));
+
+    return tokens;
+}
+
+String.standardFormatters = {
+    d: function(substitution)
+    {
+        return !isNaN(substitution) ? substitution : 0;
+    },
+
+    f: function(substitution, token)
+    {
+        if (substitution && token.precision > -1)
+            substitution = substitution.toFixed(token.precision);
+        return !isNaN(substitution) ? substitution : (token.precision > -1 ? Number(0).toFixed(token.precision) : 0);
+    },
+
+    s: function(substitution)
+    {
+        return substitution;
+    }
+}
+
+String.vsprintf = function(format, substitutions)
+{
+    return String.format(format, substitutions, String.standardFormatters, "", function(a, b) { return a + b; }).formattedResult;
+}
+
+String.format = function(format, substitutions, formatters, initialValue, append)
+{
+    if (!format || !substitutions || !substitutions.length)
+        return { formattedResult: append(initialValue, format), unusedSubstitutions: substitutions };
+
+    function prettyFunctionName()
+    {
+        return "String.format(\"" + format + "\", \"" + substitutions.join("\", \"") + "\")";
+    }
+
+    function warn(msg)
+    {
+        console.warn(prettyFunctionName() + ": " + msg);
+    }
+
+    function error(msg)
+    {
+        console.error(prettyFunctionName() + ": " + msg);
+    }
+
+    var result = initialValue;
+    var tokens = String.tokenizeFormatString(format, formatters);
+    var usedSubstitutionIndexes = {};
+
+    for (var i = 0; i < tokens.length; ++i) {
+        var token = tokens[i];
+
+        if (token.type === "string") {
+            result = append(result, token.value);
+            continue;
+        }
+
+        if (token.type !== "specifier") {
+            error("Unknown token type \"" + token.type + "\" found.");
+            continue;
+        }
+
+        if (token.substitutionIndex >= substitutions.length) {
+            // If there are not enough substitutions for the current substitutionIndex
+            // just output the format specifier literally and move on.
+            error("not enough substitution arguments. Had " + substitutions.length + " but needed " + (token.substitutionIndex + 1) + ", so substitution was skipped.");
+            result = append(result, "%" + (token.precision > -1 ? token.precision : "") + token.specifier);
+            continue;
+        }
+
+        usedSubstitutionIndexes[token.substitutionIndex] = true;
+
+        if (!(token.specifier in formatters)) {
+            // Encountered an unsupported format character, treat as a string.
+            warn("unsupported format character \u201C" + token.specifier + "\u201D. Treating as a string.");
+            result = append(result, substitutions[token.substitutionIndex]);
+            continue;
+        }
+
+        result = append(result, formatters[token.specifier](substitutions[token.substitutionIndex], token));
+    }
+
+    var unusedSubstitutions = [];
+    for (var i = 0; i < substitutions.length; ++i) {
+        if (i in usedSubstitutionIndexes)
+            continue;
+        unusedSubstitutions.push(substitutions[i]);
+    }
+
+    return { formattedResult: result, unusedSubstitutions: unusedSubstitutions };
+}
+
+/**
+ * @param {string} query
+ * @param {boolean} caseSensitive
+ * @param {boolean} isRegex
+ * @return {RegExp}
+ */
+function createSearchRegex(query, caseSensitive, isRegex)
+{
+    var regexFlags = caseSensitive ? "g" : "gi";
+    var regexObject;
+
+    if (isRegex) {
+        try {
+            regexObject = new RegExp(query, regexFlags);
+        } catch (e) {
+            // Silent catch.
+        }
+    }
+
+    if (!regexObject)
+        regexObject = createPlainTextSearchRegex(query, regexFlags);
+
+    return regexObject;
+}
+
+/**
+ * @param {string} query
+ * @param {string=} flags
+ * @return {!RegExp}
+ */
+function createPlainTextSearchRegex(query, flags)
+{
+    // This should be kept the same as the one in ContentSearchUtils.cpp.
+    var regexSpecialCharacters = String.regexSpecialCharacters();
+    var regex = "";
+    for (var i = 0; i < query.length; ++i) {
+        var c = query.charAt(i);
+        if (regexSpecialCharacters.indexOf(c) != -1)
+            regex += "\\";
+        regex += c;
+    }
+    return new RegExp(regex, flags || "");
+}
+
+/**
+ * @param {RegExp} regex
+ * @param {string} content
+ * @return {number}
+ */
+function countRegexMatches(regex, content)
+{
+    var text = content;
+    var result = 0;
+    var match;
+    while (text && (match = regex.exec(text))) {
+        if (match[0].length > 0)
+            ++result;
+        text = text.substring(match.index + 1);
+    }
+    return result;
+}
+
+/**
+ * @param {number} value
+ * @param {number} symbolsCount
+ * @return {string}
+ */
+function numberToStringWithSpacesPadding(value, symbolsCount)
+{
+    var numberString = value.toString();
+    var paddingLength = Math.max(0, symbolsCount - numberString.length);
+    var paddingString = Array(paddingLength + 1).join("\u00a0");
+    return paddingString + numberString;
+}
+
+/**
+  * @return {string}
+  */
+var createObjectIdentifier = function()
+{
+    // It has to be string for better performance.
+    return '_' + ++createObjectIdentifier._last;
+}
+
+createObjectIdentifier._last = 0;
+
+/**
+ * @constructor
+ */
+var Set = function()
+{
+    /** @type !Object.<string, Object> */
+    this._set = {};
+    this._size = 0;
+}
+
+Set.prototype = {
+    /**
+     * @param {!Object} item
+     */
+    add: function(item)
+    {
+        var objectIdentifier = item.__identifier;
+        if (!objectIdentifier) {
+            objectIdentifier = createObjectIdentifier();
+            item.__identifier = objectIdentifier;
+        }
+        if (!this._set[objectIdentifier])
+            ++this._size;
+        this._set[objectIdentifier] = item;
+    },
+    
+    /**
+     * @param {!Object} item
+     */
+    remove: function(item)
+    {
+        if (this._set[item.__identifier]) {
+            --this._size;
+            delete this._set[item.__identifier];
+        }
+    },
+
+    /**
+     * @return {!Array.<Object>}
+     */
+    items: function()
+    {
+        var result = new Array(this._size);
+        var i = 0;
+        for (var objectIdentifier in this._set)
+            result[i++] = this._set[objectIdentifier];
+        return result;
+    },
+
+    /**
+     * @param {!Object} item
+     * @return {?Object}
+     */
+    hasItem: function(item)
+    {
+        return this._set[item.__identifier];
+    },
+
+    /**
+     * @return {number}
+     */
+    size: function()
+    {
+        return this._size;
+    },
+
+    clear: function()
+    {
+        this._set = {};
+        this._size = 0;
+    }
+}
+
+/**
+ * @constructor
+ */
+var Map = function()
+{
+    this._map = {};
+    this._size = 0;
+}
+
+Map.prototype = {
+    /**
+     * @param {Object} key
+     * @param {*=} value
+     */
+    put: function(key, value)
+    {
+        var objectIdentifier = key.__identifier;
+        if (!objectIdentifier) {
+            objectIdentifier = createObjectIdentifier();
+            key.__identifier = objectIdentifier;
+        }
+        if (!this._map[objectIdentifier])
+            ++this._size;
+        this._map[objectIdentifier] = [key, value];
+    },
+    
+    /**
+     * @param {Object} key
+     */
+    remove: function(key)
+    {
+        var result = this._map[key.__identifier];
+        if (!result)
+            return undefined;
+        --this._size;
+        delete this._map[key.__identifier];
+        return result[1];
+    },
+
+    /**
+     * @return {Array.<Object>}
+     */
+    keys: function()
+    {
+        return this._list(0);
+    },
+
+    values: function()
+    {
+        return this._list(1);
+    },
+
+    /**
+     * @param {number} index
+     */
+    _list: function(index)
+    {
+        var result = new Array(this._size);
+        var i = 0;
+        for (var objectIdentifier in this._map)
+            result[i++] = this._map[objectIdentifier][index];
+        return result;
+    },
+
+    /**
+     * @param {Object} key
+     */
+    get: function(key)
+    {
+        var entry = this._map[key.__identifier];
+        return entry ? entry[1] : undefined;
+    },
+
+    /**
+     * @param {Object} key
+     */
+    contains: function(key)
+    {
+        var entry = this._map[key.__identifier];
+        return !!entry;
+    },
+
+    size: function()
+    {
+        return this._size;
+    },
+
+    clear: function()
+    {
+        this._map = {};
+        this._size = 0;
+    }
+}
+/**
+ * @param {string} url
+ * @param {boolean=} async
+ * @param {function(?string)=} callback
+ * @return {?string}
+ */
+function loadXHR(url, async, callback) 
+{
+    function onReadyStateChanged() 
+    {
+        if (xhr.readyState !== XMLHttpRequest.DONE)
+            return;
+
+        if (xhr.status === 200) {
+            callback(xhr.responseText);
+            return;
+        }
+
+        callback(null); 
+   }
+
+    var xhr = new XMLHttpRequest();
+    xhr.open("GET", url, async);
+    if (async)
+        xhr.onreadystatechange = onReadyStateChanged;        
+    xhr.send(null);
+
+    if (!async) {
+        if (xhr.status === 200) 
+            return xhr.responseText;
+        return null;
+    }
+    return null;
+}
+
+/**
+ * @constructor
+ */
+function StringPool()
+{
+    this.reset();
+}
+
+StringPool.prototype = {
+    /**
+     * @param {string} string
+     * @return {string}
+     */
+    intern: function(string)
+    {
+        // Do not mess with setting __proto__ to anything but null, just handle it explicitly.
+        if (string === "__proto__")
+            return "__proto__";
+        var result = this._strings[string];
+        if (result === undefined) {
+            this._strings[string] = string;
+            result = string;
+        }
+        return result;
+    },
+
+    reset: function()
+    {
+        this._strings = Object.create(null);
+    },
+
+    /**
+     * @param {Object} obj
+     * @param {number=} depthLimit
+     */
+    internObjectStrings: function(obj, depthLimit)
+    {
+        if (typeof depthLimit !== "number")
+            depthLimit = 100;
+        else if (--depthLimit < 0)
+            throw "recursion depth limit reached in StringPool.deepIntern(), perhaps attempting to traverse cyclical references?";
+
+        for (var field in obj) {
+            switch (typeof obj[field]) {
+            case "string":
+                obj[field] = this.intern(obj[field]);
+                break;
+            case "object":
+                this.internObjectStrings(obj[field], depthLimit);
+                break;
+            }
+        }
+    }
+}
+
+var _importedScripts = {};
+
+/**
+ * This function behavior depends on the "debug_devtools" flag value.
+ * - In debug mode it loads scripts synchronously via xhr request.
+ * - In release mode every occurrence of "importScript" in the js files
+ *   that have been white listed in the build system gets replaced with
+ *   the script source code on the compilation phase.
+ *   The build system will throw an exception if it found importScript call
+ *   in other files.
+ *
+ * To load scripts lazily in release mode call "loadScript" function.
+ * @param {string} scriptName
+ */
+function importScript(scriptName)
+{
+    if (_importedScripts[scriptName])
+        return;
+    var xhr = new XMLHttpRequest();
+    _importedScripts[scriptName] = true;
+    if (window.flattenImports)
+        scriptName = scriptName.split("/").reverse()[0];
+    xhr.open("GET", scriptName, false);
+    xhr.send(null);
+    if (!xhr.responseText)
+        throw "empty response arrived for script '" + scriptName + "'";
+    var sourceURL = WebInspector.ParsedURL.completeURL(window.location.href, scriptName); 
+    window.eval(xhr.responseText + "\n//@ sourceURL=" + sourceURL);
+}
+
+var loadScript = importScript;
diff --git a/Source/devtools/frontend_protocol_sources.target.darwin-arm.mk b/Source/devtools/frontend_protocol_sources.target.darwin-arm.mk
new file mode 100644
index 0000000..80313f5
--- /dev/null
+++ b/Source/devtools/frontend_protocol_sources.target.darwin-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+LOCAL_MODULE_STEM := frontend_protocol_sources
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "generateInspectorProtocolFrontendSources":
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/CodeGeneratorFrontend.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/protocol.json $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: Generating Inspector protocol frontend sources from protocol.json ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/webcore; python scripts/CodeGeneratorFrontend.py protocol.json --output_js_dir "$(gyp_shared_intermediate_dir)/webcore"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+
+# Alias gyp target name.
+.PHONY: frontend_protocol_sources
+frontend_protocol_sources: third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/frontend_protocol_sources.target.darwin-x86.mk b/Source/devtools/frontend_protocol_sources.target.darwin-x86.mk
new file mode 100644
index 0000000..80313f5
--- /dev/null
+++ b/Source/devtools/frontend_protocol_sources.target.darwin-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+LOCAL_MODULE_STEM := frontend_protocol_sources
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "generateInspectorProtocolFrontendSources":
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/CodeGeneratorFrontend.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/protocol.json $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: Generating Inspector protocol frontend sources from protocol.json ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/webcore; python scripts/CodeGeneratorFrontend.py protocol.json --output_js_dir "$(gyp_shared_intermediate_dir)/webcore"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+
+# Alias gyp target name.
+.PHONY: frontend_protocol_sources
+frontend_protocol_sources: third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/frontend_protocol_sources.target.linux-arm.mk b/Source/devtools/frontend_protocol_sources.target.linux-arm.mk
new file mode 100644
index 0000000..80313f5
--- /dev/null
+++ b/Source/devtools/frontend_protocol_sources.target.linux-arm.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+LOCAL_MODULE_STEM := frontend_protocol_sources
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "generateInspectorProtocolFrontendSources":
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/CodeGeneratorFrontend.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/protocol.json $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: Generating Inspector protocol frontend sources from protocol.json ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/webcore; python scripts/CodeGeneratorFrontend.py protocol.json --output_js_dir "$(gyp_shared_intermediate_dir)/webcore"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+
+# Alias gyp target name.
+.PHONY: frontend_protocol_sources
+frontend_protocol_sources: third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/frontend_protocol_sources.target.linux-x86.mk b/Source/devtools/frontend_protocol_sources.target.linux-x86.mk
new file mode 100644
index 0000000..80313f5
--- /dev/null
+++ b/Source/devtools/frontend_protocol_sources.target.linux-x86.mk
@@ -0,0 +1,50 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+LOCAL_MODULE_STEM := frontend_protocol_sources
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+### Rules for action "generateInspectorProtocolFrontendSources":
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/CodeGeneratorFrontend.py $(LOCAL_PATH)/third_party/WebKit/Source/devtools/protocol.json $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: Generating Inspector protocol frontend sources from protocol.json ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/webcore; python scripts/CodeGeneratorFrontend.py protocol.json --output_js_dir "$(gyp_shared_intermediate_dir)/webcore"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/webcore/InspectorBackendCommands.js
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+
+# Alias gyp target name.
+.PHONY: frontend_protocol_sources
+frontend_protocol_sources: third_party_WebKit_Source_devtools_frontend_protocol_sources_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/generate_devtools_grd.target.darwin-arm.mk b/Source/devtools/generate_devtools_grd.target.darwin-arm.mk
new file mode 100644
index 0000000..e8bfbe1
--- /dev/null
+++ b/Source/devtools/generate_devtools_grd.target.darwin-arm.mk
@@ -0,0 +1,64 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+LOCAL_MODULE_STEM := generate_devtools_grd
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_extension_api_gyp)/devtools_extension_api.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp)/concatenated_devtools_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp)/concatenated_devtools_elements_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp)/concatenated_devtools_resources_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp)/concatenated_devtools_network_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp)/concatenated_devtools_scripts_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp)/concatenated_devtools_timeline_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp)/concatenated_devtools_profiles_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp)/concatenated_devtools_audits_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp)/concatenated_devtools_codemirror_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp)/concatenated_heap_snapshot_worker_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp)/concatenated_script_formatter_worker_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp)/concatenated_devtools_css.stamp
+
+### Rules for action "generate_devtools_grd":
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_grd.py $(gyp_shared_intermediate_dir)/resources/inspector/devtools.html $(gyp_shared_intermediate_dir)/resources/inspector/inspector.js $(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js $(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js $(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js $(gyp_shared_intermediate_dir)/resources/inspector/inspector.css $(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/addIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/applicationCache.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/back.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpoint2.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpoint2_2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditional2.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditional2_2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditionalBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditionalCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/checker.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/cookie.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/namedFlowOverflow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/database.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/databaseTable.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/deleteIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/domain.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/forward.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/fileSystem.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/frame.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeader.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderPressed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderSelectedPressed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/graphLabelCalloutLeft.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/graphLabelCalloutRight.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDB.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDBObjectStore.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDBIndex.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/localStorage.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneAddButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneElementStateButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneFilterButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneRefreshButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneSettingsButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/popoverArrows.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/popoverBackground.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileGroupIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileSmallIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/programCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/radioDot.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionEmpty.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionFit.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionOverset.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceCSSIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceDocumentIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceDocumentIconSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceJSIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcePlainIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcePlainIconSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcesTimeGraphIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallBrightBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallWhite.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchNext.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchPrev.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segment.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentHover.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentHoverEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentSelectedEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/sessionStorage.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinner.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerActive.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerActiveSelected.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerInactive.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerInactiveSelected.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarButtonGlyphs.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarButtonGlyphs2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarResizerHorizontal.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarResizerVertical.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbActiveHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbActiveVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoverHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoverVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillGreen.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillOrange.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillPurple.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillRed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillYellow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillGreen.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillOrange.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillPurple.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillRed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillYellow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarIcons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarIconsSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarItemSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/trackHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/trackVert.png $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_generate_devtools_grd_target_generate_devtools_grd ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/devtools; python scripts/generate_devtools_grd.py "$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js" "$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css" "$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js" front_end/auditsPanel.css front_end/breadcrumbList.css front_end/breakpointsList.css front_end/buildSystemOnly.js front_end/cm/cmdevtools.css front_end/cm/codemirror.css front_end/cssNamedFlows.css front_end/dataGrid.css front_end/elementsPanel.css front_end/filteredItemSelectionDialog.css front_end/flameChart.css front_end/heapProfiler.css front_end/helpScreen.css front_end/indexedDBViews.css front_end/inspectorCommon.css front_end/nativeMemoryProfiler.css front_end/navigatorView.css front_end/networkLogView.css front_end/networkPanel.css front_end/panelEnablerView.css front_end/profilesPanel.css front_end/resourceView.css front_end/resourcesPanel.css front_end/revisionHistory.css front_end/scriptsPanel.css front_end/sidebarPane.css front_end/spectrum.css front_end/splitView.css front_end/tabbedPane.css front_end/textEditor.css front_end/textPrompt.css front_end/timelinePanel.css front_end/canvasProfiler.css --images front_end/Images --output "$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+
+# Alias gyp target name.
+.PHONY: generate_devtools_grd
+generate_devtools_grd: third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/generate_devtools_grd.target.darwin-x86.mk b/Source/devtools/generate_devtools_grd.target.darwin-x86.mk
new file mode 100644
index 0000000..e8bfbe1
--- /dev/null
+++ b/Source/devtools/generate_devtools_grd.target.darwin-x86.mk
@@ -0,0 +1,64 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+LOCAL_MODULE_STEM := generate_devtools_grd
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_extension_api_gyp)/devtools_extension_api.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp)/concatenated_devtools_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp)/concatenated_devtools_elements_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp)/concatenated_devtools_resources_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp)/concatenated_devtools_network_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp)/concatenated_devtools_scripts_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp)/concatenated_devtools_timeline_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp)/concatenated_devtools_profiles_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp)/concatenated_devtools_audits_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp)/concatenated_devtools_codemirror_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp)/concatenated_heap_snapshot_worker_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp)/concatenated_script_formatter_worker_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp)/concatenated_devtools_css.stamp
+
+### Rules for action "generate_devtools_grd":
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_grd.py $(gyp_shared_intermediate_dir)/resources/inspector/devtools.html $(gyp_shared_intermediate_dir)/resources/inspector/inspector.js $(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js $(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js $(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js $(gyp_shared_intermediate_dir)/resources/inspector/inspector.css $(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/addIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/applicationCache.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/back.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpoint2.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpoint2_2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditional2.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditional2_2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditionalBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditionalCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/checker.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/cookie.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/namedFlowOverflow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/database.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/databaseTable.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/deleteIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/domain.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/forward.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/fileSystem.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/frame.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeader.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderPressed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderSelectedPressed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/graphLabelCalloutLeft.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/graphLabelCalloutRight.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDB.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDBObjectStore.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDBIndex.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/localStorage.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneAddButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneElementStateButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneFilterButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneRefreshButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneSettingsButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/popoverArrows.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/popoverBackground.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileGroupIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileSmallIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/programCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/radioDot.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionEmpty.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionFit.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionOverset.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceCSSIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceDocumentIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceDocumentIconSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceJSIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcePlainIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcePlainIconSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcesTimeGraphIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallBrightBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallWhite.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchNext.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchPrev.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segment.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentHover.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentHoverEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentSelectedEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/sessionStorage.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinner.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerActive.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerActiveSelected.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerInactive.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerInactiveSelected.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarButtonGlyphs.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarButtonGlyphs2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarResizerHorizontal.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarResizerVertical.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbActiveHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbActiveVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoverHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoverVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillGreen.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillOrange.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillPurple.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillRed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillYellow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillGreen.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillOrange.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillPurple.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillRed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillYellow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarIcons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarIconsSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarItemSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/trackHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/trackVert.png $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_generate_devtools_grd_target_generate_devtools_grd ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/devtools; python scripts/generate_devtools_grd.py "$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js" "$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css" "$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js" front_end/auditsPanel.css front_end/breadcrumbList.css front_end/breakpointsList.css front_end/buildSystemOnly.js front_end/cm/cmdevtools.css front_end/cm/codemirror.css front_end/cssNamedFlows.css front_end/dataGrid.css front_end/elementsPanel.css front_end/filteredItemSelectionDialog.css front_end/flameChart.css front_end/heapProfiler.css front_end/helpScreen.css front_end/indexedDBViews.css front_end/inspectorCommon.css front_end/nativeMemoryProfiler.css front_end/navigatorView.css front_end/networkLogView.css front_end/networkPanel.css front_end/panelEnablerView.css front_end/profilesPanel.css front_end/resourceView.css front_end/resourcesPanel.css front_end/revisionHistory.css front_end/scriptsPanel.css front_end/sidebarPane.css front_end/spectrum.css front_end/splitView.css front_end/tabbedPane.css front_end/textEditor.css front_end/textPrompt.css front_end/timelinePanel.css front_end/canvasProfiler.css --images front_end/Images --output "$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+
+# Alias gyp target name.
+.PHONY: generate_devtools_grd
+generate_devtools_grd: third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/generate_devtools_grd.target.linux-arm.mk b/Source/devtools/generate_devtools_grd.target.linux-arm.mk
new file mode 100644
index 0000000..e8bfbe1
--- /dev/null
+++ b/Source/devtools/generate_devtools_grd.target.linux-arm.mk
@@ -0,0 +1,64 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+LOCAL_MODULE_STEM := generate_devtools_grd
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_extension_api_gyp)/devtools_extension_api.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp)/concatenated_devtools_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp)/concatenated_devtools_elements_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp)/concatenated_devtools_resources_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp)/concatenated_devtools_network_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp)/concatenated_devtools_scripts_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp)/concatenated_devtools_timeline_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp)/concatenated_devtools_profiles_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp)/concatenated_devtools_audits_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp)/concatenated_devtools_codemirror_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp)/concatenated_heap_snapshot_worker_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp)/concatenated_script_formatter_worker_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp)/concatenated_devtools_css.stamp
+
+### Rules for action "generate_devtools_grd":
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_grd.py $(gyp_shared_intermediate_dir)/resources/inspector/devtools.html $(gyp_shared_intermediate_dir)/resources/inspector/inspector.js $(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js $(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js $(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js $(gyp_shared_intermediate_dir)/resources/inspector/inspector.css $(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/addIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/applicationCache.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/back.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpoint2.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpoint2_2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditional2.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditional2_2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditionalBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditionalCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/checker.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/cookie.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/namedFlowOverflow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/database.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/databaseTable.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/deleteIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/domain.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/forward.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/fileSystem.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/frame.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeader.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderPressed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderSelectedPressed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/graphLabelCalloutLeft.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/graphLabelCalloutRight.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDB.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDBObjectStore.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDBIndex.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/localStorage.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneAddButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneElementStateButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneFilterButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneRefreshButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneSettingsButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/popoverArrows.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/popoverBackground.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileGroupIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileSmallIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/programCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/radioDot.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionEmpty.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionFit.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionOverset.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceCSSIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceDocumentIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceDocumentIconSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceJSIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcePlainIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcePlainIconSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcesTimeGraphIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallBrightBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallWhite.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchNext.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchPrev.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segment.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentHover.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentHoverEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentSelectedEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/sessionStorage.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinner.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerActive.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerActiveSelected.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerInactive.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerInactiveSelected.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarButtonGlyphs.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarButtonGlyphs2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarResizerHorizontal.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarResizerVertical.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbActiveHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbActiveVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoverHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoverVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillGreen.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillOrange.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillPurple.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillRed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillYellow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillGreen.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillOrange.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillPurple.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillRed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillYellow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarIcons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarIconsSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarItemSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/trackHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/trackVert.png $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_generate_devtools_grd_target_generate_devtools_grd ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/devtools; python scripts/generate_devtools_grd.py "$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js" "$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css" "$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js" front_end/auditsPanel.css front_end/breadcrumbList.css front_end/breakpointsList.css front_end/buildSystemOnly.js front_end/cm/cmdevtools.css front_end/cm/codemirror.css front_end/cssNamedFlows.css front_end/dataGrid.css front_end/elementsPanel.css front_end/filteredItemSelectionDialog.css front_end/flameChart.css front_end/heapProfiler.css front_end/helpScreen.css front_end/indexedDBViews.css front_end/inspectorCommon.css front_end/nativeMemoryProfiler.css front_end/navigatorView.css front_end/networkLogView.css front_end/networkPanel.css front_end/panelEnablerView.css front_end/profilesPanel.css front_end/resourceView.css front_end/resourcesPanel.css front_end/revisionHistory.css front_end/scriptsPanel.css front_end/sidebarPane.css front_end/spectrum.css front_end/splitView.css front_end/tabbedPane.css front_end/textEditor.css front_end/textPrompt.css front_end/timelinePanel.css front_end/canvasProfiler.css --images front_end/Images --output "$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+
+# Alias gyp target name.
+.PHONY: generate_devtools_grd
+generate_devtools_grd: third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/generate_devtools_grd.target.linux-x86.mk b/Source/devtools/generate_devtools_grd.target.linux-x86.mk
new file mode 100644
index 0000000..e8bfbe1
--- /dev/null
+++ b/Source/devtools/generate_devtools_grd.target.linux-x86.mk
@@ -0,0 +1,64 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := GYP
+LOCAL_MODULE := third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+LOCAL_MODULE_STEM := generate_devtools_grd
+LOCAL_MODULE_SUFFIX := .stamp
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_html_gyp)/devtools_html.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_devtools_extension_api_gyp)/devtools_extension_api.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_js_gyp)/concatenated_devtools_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_elements_js_gyp)/concatenated_devtools_elements_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_resources_js_gyp)/concatenated_devtools_resources_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_network_js_gyp)/concatenated_devtools_network_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_scripts_js_gyp)/concatenated_devtools_scripts_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_timeline_js_gyp)/concatenated_devtools_timeline_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_profiles_js_gyp)/concatenated_devtools_profiles_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_audits_js_gyp)/concatenated_devtools_audits_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_codemirror_js_gyp)/concatenated_devtools_codemirror_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_heap_snapshot_worker_js_gyp)/concatenated_heap_snapshot_worker_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_script_formatter_worker_js_gyp)/concatenated_script_formatter_worker_js.stamp \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_Source_devtools_concatenated_devtools_css_gyp)/concatenated_devtools_css.stamp
+
+### Rules for action "generate_devtools_grd":
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_intermediate_dir)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: gyp_shared_intermediate_dir := $(GYP_ABS_ANDROID_TOP_DIR)/$(gyp_shared_intermediate_dir)
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd: $(LOCAL_PATH)/third_party/WebKit/Source/devtools/scripts/generate_devtools_grd.py $(gyp_shared_intermediate_dir)/resources/inspector/devtools.html $(gyp_shared_intermediate_dir)/resources/inspector/inspector.js $(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js $(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js $(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js $(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js $(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js $(gyp_shared_intermediate_dir)/resources/inspector/inspector.css $(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/auditsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breadcrumbList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/breakpointsList.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/buildSystemOnly.js $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/cmdevtools.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cm/codemirror.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/cssNamedFlows.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/dataGrid.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/elementsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/filteredItemSelectionDialog.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/flameChart.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/heapProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/helpScreen.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/indexedDBViews.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/inspectorCommon.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/nativeMemoryProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/navigatorView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkLogView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/networkPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/panelEnablerView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/profilesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourceView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/resourcesPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/revisionHistory.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/scriptsPanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/sidebarPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/spectrum.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/splitView.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/tabbedPane.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textEditor.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/textPrompt.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/timelinePanel.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/canvasProfiler.css $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/addIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/applicationCache.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/back.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpoint2.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpoint2_2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditional2.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditional2_2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditionalBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointConditionalCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/breakpointCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/checker.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/cookie.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/namedFlowOverflow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/database.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/databaseTable.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/deleteIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/domain.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/forward.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/fileSystem.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/frame.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeader.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderPressed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/glossyHeaderSelectedPressed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/graphLabelCalloutLeft.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/graphLabelCalloutRight.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDB.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDBObjectStore.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/indexedDBIndex.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/localStorage.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneAddButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneElementStateButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneFilterButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneRefreshButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/paneSettingsButtons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/popoverArrows.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/popoverBackground.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileGroupIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/profileSmallIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/programCounterBorder.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/radioDot.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionEmpty.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionFit.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/regionOverset.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceCSSIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceDocumentIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceDocumentIconSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourceJSIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcePlainIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcePlainIconSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/resourcesTimeGraphIcon.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallBrightBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchSmallWhite.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchNext.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/searchPrev.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segment.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentHover.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentHoverEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/segmentSelectedEnd.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/sessionStorage.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinner.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerActive.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerActiveSelected.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerInactive.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/spinnerInactiveSelected.gif $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarButtonGlyphs.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarButtonGlyphs2x.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarResizerHorizontal.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/statusbarResizerVertical.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbActiveHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbActiveVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoverHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/thumbHoverVert.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillGreen.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillOrange.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillPurple.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillRed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelineHollowPillYellow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillBlue.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillGray.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillGreen.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillOrange.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillPurple.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillRed.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/timelinePillYellow.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarIcons.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarIconsSmall.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/toolbarItemSelected.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/trackHoriz.png $(LOCAL_PATH)/third_party/WebKit/Source/devtools/front_end/Images/trackVert.png $(GYP_TARGET_DEPENDENCIES)
+	@echo "Gyp action: third_party_WebKit_Source_devtools_devtools_gyp_generate_devtools_grd_target_generate_devtools_grd ($@)"
+	$(hide)cd $(gyp_local_path)/third_party/WebKit/Source/devtools; mkdir -p $(gyp_shared_intermediate_dir)/devtools; python scripts/generate_devtools_grd.py "$(gyp_shared_intermediate_dir)/resources/inspector/devtools.html" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ElementsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ResourcesPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/NetworkPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/TimelinePanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ProfilesPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/AuditsPanel.js" "$(gyp_shared_intermediate_dir)/resources/inspector/CodeMirrorTextEditor.js" "$(gyp_shared_intermediate_dir)/resources/inspector/HeapSnapshotWorker.js" "$(gyp_shared_intermediate_dir)/resources/inspector/ScriptFormatterWorker.js" "$(gyp_shared_intermediate_dir)/resources/inspector/inspector.css" "$(gyp_shared_intermediate_dir)/resources/inspector/devtools_extension_api.js" front_end/auditsPanel.css front_end/breadcrumbList.css front_end/breakpointsList.css front_end/buildSystemOnly.js front_end/cm/cmdevtools.css front_end/cm/codemirror.css front_end/cssNamedFlows.css front_end/dataGrid.css front_end/elementsPanel.css front_end/filteredItemSelectionDialog.css front_end/flameChart.css front_end/heapProfiler.css front_end/helpScreen.css front_end/indexedDBViews.css front_end/inspectorCommon.css front_end/nativeMemoryProfiler.css front_end/navigatorView.css front_end/networkLogView.css front_end/networkPanel.css front_end/panelEnablerView.css front_end/profilesPanel.css front_end/resourceView.css front_end/resourcesPanel.css front_end/revisionHistory.css front_end/scriptsPanel.css front_end/sidebarPane.css front_end/spectrum.css front_end/splitView.css front_end/tabbedPane.css front_end/textEditor.css front_end/textPrompt.css front_end/timelinePanel.css front_end/canvasProfiler.css --images front_end/Images --output "$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd"
+
+
+
+GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/devtools/devtools_resources.grd
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+### Rules for final target.
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+
+# Alias gyp target name.
+.PHONY: generate_devtools_grd
+generate_devtools_grd: third_party_WebKit_Source_devtools_generate_devtools_grd_gyp
+
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(hide) echo "Gyp timestamp: $@"
+	$(hide) mkdir -p $(dir $@)
+	$(hide) touch $@
diff --git a/Source/devtools/protocol.json b/Source/devtools/protocol.json
new file mode 100644
index 0000000..c3e48c8
--- /dev/null
+++ b/Source/devtools/protocol.json
@@ -0,0 +1,3727 @@
+{
+    "version": { "major": "1", "minor": "0" },
+    "domains": [{
+        "domain": "Inspector",
+        "hidden": true,
+        "types": [],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables inspector domain notifications."
+            },
+            {
+                "name": "disable",
+                "description": "Disables inspector domain notifications."
+            }
+        ],
+        "events": [
+            {
+                "name": "evaluateForTestInFrontend",
+                "parameters": [
+                    { "name": "testCallId", "type": "integer" },
+                    { "name": "script", "type": "string" }
+                ]
+            },
+            {
+                "name": "inspect",
+                "parameters": [
+                    { "name": "object", "$ref": "Runtime.RemoteObject" },
+                    { "name": "hints", "type": "object" }
+                ]
+            },
+            {
+                "name": "detached",
+                "description": "Fired when remote debugging connection is about to be terminated. Contains detach reason.",
+                "parameters": [
+                    { "name": "reason", "type": "string", "description": "The reason why connection has been terminated." }
+                ]
+            },
+            {
+                "name": "targetCrashed",
+                "description": "Fired when debugging target has crashed"
+            }
+        ]
+    },
+    {
+        "domain": "Memory",
+        "hidden": true,
+        "types": [
+            {
+                "id": "MemoryBlock",
+                "type": "object",
+                "properties": [
+                    { "name": "size", "type": "number", "optional": true, "description": "Size of the block in bytes if available" },
+                    { "name": "name", "type": "string", "description": "Unique name used to identify the component that allocated this block" },
+                    { "name": "children", "type": "array", "optional": true, "items": { "$ref": "MemoryBlock" }}
+                ]
+            },
+            {
+                "id": "HeapSnapshotChunk",
+                "type": "object",
+                "properties": [
+                    { "name": "strings", "type": "array", "items": { "type": "string" }, "description": "An array of strings that were found since last update." },
+                    { "name": "nodes", "type": "array", "items": { "type": "integer" }, "description": "An array of nodes that were found since last update." },
+                    { "name": "edges", "type": "array", "items": { "type": "integer" }, "description": "An array of edges that were found since last update." },
+                    { "name": "baseToRealNodeId", "type": "array", "items": { "type": "integer" }, "description": "An array of integers for nodeId remapping. Even nodeId has to be mapped to the following odd nodeId." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "getDOMCounters",
+                "returns": [
+                    { "name": "documents", "type": "integer" },
+                    { "name": "nodes", "type": "integer" },
+                    { "name": "jsEventListeners", "type": "integer" }
+                ]
+            },
+            {
+                "name": "getProcessMemoryDistribution",
+                "parameters": [
+                    { "name": "reportGraph", "type": "boolean", "optional": true, "description": "Whether native memory graph should be reported in addition to aggregated statistics." }
+                ],
+                "returns": [
+                    { "name": "distribution", "$ref": "MemoryBlock", "description": "An object describing all memory allocated by the process"},
+                    { "name": "graphMetaInformation", "type": "object", "optional": true, "description": "An object describing structures of nodes and edges in the graph."}
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "addNativeSnapshotChunk",
+                "parameters": [
+                    { "name": "chunk", "$ref": "HeapSnapshotChunk", "description": "A chunk of the serialized the snapshot." }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "Page",
+        "description": "Actions and events related to the inspected page belong to the page domain.",
+        "types": [
+            {
+                "id": "ResourceType",
+                "type": "string",
+                "enum": ["Document", "Stylesheet", "Image", "Font", "Script", "XHR", "WebSocket", "Other"],
+                "description": "Resource type as it was perceived by the rendering engine."
+            },
+            {
+                "id": "Frame",
+                "type": "object",
+                "description": "Information about the Frame on the page.",
+                "properties": [
+                    { "name": "id", "type": "string", "description": "Frame unique identifier." },
+                    { "name": "parentId", "type": "string", "optional": true, "description": "Parent frame identifier." },
+                    { "name": "loaderId", "$ref": "Network.LoaderId", "description": "Identifier of the loader associated with this frame." },
+                    { "name": "name", "type": "string", "optional": true, "description": "Frame's name as specified in the tag." },
+                    { "name": "url", "type": "string", "description": "Frame document's URL." },
+                    { "name": "securityOrigin", "type": "string", "description": "Frame document's security origin." },
+                    { "name": "mimeType", "type": "string", "description": "Frame document's mimeType as determined by the browser." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "FrameResourceTree",
+                "type": "object",
+                "description": "Information about the Frame hierarchy along with their cached resources.",
+                "properties": [
+                    { "name": "frame", "$ref": "Frame", "description": "Frame information for this tree item." },
+                    { "name": "childFrames", "type": "array", "optional": true, "items": { "$ref": "FrameResourceTree" }, "description": "Child frames." },
+                    { "name": "resources", "type": "array",
+                        "items": {
+                            "type": "object",
+                            "properties": [
+                                { "name": "url", "type": "string", "description": "Resource URL." },
+                                { "name": "type", "$ref": "ResourceType", "description": "Type of this resource." },
+                                { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." },
+                                { "name": "failed", "type": "boolean", "optional": true, "description": "True if the resource failed to load." },
+                                { "name": "canceled", "type": "boolean", "optional": true, "description": "True if the resource was canceled during loading." }
+                            ]
+                        },
+                        "description": "Information about frame resources."
+                    }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "SearchMatch",
+                "type": "object",
+                "description": "Search match for resource.",
+                "properties": [
+                    { "name": "lineNumber", "type": "number", "description": "Line number in resource content." },
+                    { "name": "lineContent", "type": "string", "description": "Line with match content." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "SearchResult",
+                "type": "object",
+                "description": "Search result for resource.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Resource URL." },
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Resource frame id." },
+                    { "name": "matchesCount", "type": "number", "description": "Number of matches in the resource content." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "Cookie",
+                "type": "object",
+                "description": "Cookie object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Cookie name." },
+                    { "name": "value", "type": "string", "description": "Cookie value." },
+                    { "name": "domain", "type": "string", "description": "Cookie domain." },
+                    { "name": "path", "type": "string", "description": "Cookie path." },
+                    { "name": "expires", "type": "number", "description": "Cookie expires." },
+                    { "name": "size", "type": "integer", "description": "Cookie size." },
+                    { "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." },
+                    { "name": "secure", "type": "boolean", "description": "True if cookie is secure." },
+                    { "name": "session", "type": "boolean", "description": "True in case of session cookie." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "ScriptIdentifier",
+                "type": "string",
+                "description": "Unique script identifier.",
+                "hidden": true
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables page domain notifications."
+            },
+            {
+                "name": "disable",
+                "description": "Disables page domain notifications."
+            },
+            {
+                "name": "addScriptToEvaluateOnLoad",
+                "parameters": [
+                    { "name": "scriptSource", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "identifier", "$ref": "ScriptIdentifier", "description": "Identifier of the added script." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "removeScriptToEvaluateOnLoad",
+                "parameters": [
+                    { "name": "identifier", "$ref": "ScriptIdentifier" }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "reload",
+                "parameters": [
+                    { "name": "ignoreCache", "type": "boolean", "optional": true, "description": "If true, browser cache is ignored (as if the user pressed Shift+refresh)." },
+                    { "name": "scriptToEvaluateOnLoad", "type": "string", "optional": true, "description": "If set, the script will be injected into all frames of the inspected page after reload." },
+                    { "name": "scriptPreprocessor", "type": "string", "optional": true, "description": "Script body that should evaluate to function that will preprocess all the scripts before their compilation.", "hidden": true }
+                ],
+                "description": "Reloads given page optionally ignoring the cache."
+            },
+            {
+                "name": "navigate",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL to navigate the page to." }
+                ],
+                "description": "Navigates current page to the given URL."
+            },
+            {
+                "name": "getCookies",
+                "returns": [
+                    { "name": "cookies", "type": "array", "items": { "$ref": "Cookie"}, "description": "Array of cookie objects." },
+                    { "name": "cookiesString", "type": "string", "description": "document.cookie string representation of the cookies." }
+                ],
+                "description": "Returns all browser cookies. Depending on the backend support, will either return detailed cookie information in the <code>cookie</code> field or string cookie representation using <code>cookieString</code>.",
+                "hidden": true
+            },
+            {
+                "name": "deleteCookie",
+                "parameters": [
+                    { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." },
+                    { "name": "url", "type": "string", "description": "URL to match cooke domain and path." }
+                ],
+                "description": "Deletes browser cookie with given name, domain and path.",
+                "hidden": true
+            },
+            {
+                "name": "getResourceTree",
+                "description": "Returns present frame / resource tree structure.",
+                "returns": [
+                    { "name": "frameTree", "$ref": "FrameResourceTree", "description": "Present frame / resource tree structure." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "getResourceContent",
+                "description": "Returns content of the given resource.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame id to get resource for." },
+                    { "name": "url", "type": "string", "description": "URL of the resource to get content for." }
+                ],
+                "returns": [
+                    { "name": "content", "type": "string", "description": "Resource content." },
+                    { "name": "base64Encoded", "type": "boolean", "description": "True, if content was served as base64." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "searchInResource",
+                "description": "Searches for given string in resource content.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame id for resource to search in." },
+                    { "name": "url", "type": "string", "description": "URL of the resource to search in." },
+                    { "name": "query", "type": "string", "description": "String to search for."  },
+                    { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." },
+                    { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "searchInResources",
+                "description": "Searches for given string in frame / resource tree structure.",
+                "parameters": [
+                    { "name": "text", "type": "string", "description": "String to search for."  },
+                    { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." },
+                    { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "SearchResult" }, "description": "List of search results." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setDocumentContent",
+                "description": "Sets given markup as the document's HTML.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame id to set HTML for." },
+                    { "name": "html", "type": "string", "description": "HTML content to set."  }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setDeviceMetricsOverride",
+                "description": "Overrides the values of device screen dimensions (window.screen.width, window.screen.height, window.innerWidth, window.innerHeight, and \"device-width\"/\"device-height\"-related CSS media query results) and the font scale factor.",
+                "parameters": [
+                    { "name": "width", "type": "integer", "description": "Overriding width value in pixels (minimum 0, maximum 10000000). 0 disables the override." },
+                    { "name": "height", "type": "integer", "description": "Overriding height value in pixels (minimum 0, maximum 10000000). 0 disables the override." },
+                    { "name": "fontScaleFactor", "type": "number", "description": "Overriding font scale factor value (must be positive). 1 disables the override." },
+                    { "name": "fitWindow", "type": "boolean", "description": "Whether a view that exceeds the available browser window area should be scaled down to fit." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setShowPaintRects",
+                "description": "Requests that backend shows paint rectangles",
+                "parameters": [
+                    { "name": "result", "type": "boolean", "description": "True for showing paint rectangles" }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setShowDebugBorders",
+                "description": "Requests that backend shows debug borders on layers",
+                "parameters": [
+                    { "name": "show", "type": "boolean", "description": "True for showing debug borders" }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "canShowFPSCounter",
+                "description": "Tells if backend supports a FPS counter display",
+                "returns": [
+                    { "name": "show", "type": "boolean", "description": "True if the FPS count can be shown" }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setShowFPSCounter",
+                "description": "Requests that backend shows the FPS counter",
+                "parameters": [
+                    { "name": "show", "type": "boolean", "description": "True for showing the FPS counter" }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "canContinuouslyPaint",
+                "description": "Tells if backend supports continuous painting",
+                "returns": [
+                    { "name": "value", "type": "boolean", "description": "True if continuous painting is available" }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setContinuousPaintingEnabled",
+                "description": "Requests that backend enables continuous painting",
+                "parameters": [
+                    { "name": "enabled", "type": "boolean", "description": "True for enabling cointinuous painting" }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "getScriptExecutionStatus",
+                "description": "Determines if scripts can be executed in the page.",
+                "returns": [
+                    { "name": "result", "type": "string", "enum": ["allowed", "disabled", "forbidden"], "description": "Script execution status: \"allowed\" if scripts can be executed, \"disabled\" if script execution has been disabled through page settings, \"forbidden\" if script execution for the given page is not possible for other reasons." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setScriptExecutionDisabled",
+                "description": "Switches script execution in the page.",
+                "parameters": [
+                    { "name": "value", "type": "boolean", "description": "Whether script execution should be disabled in the page." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setGeolocationOverride",
+                "description": "Overrides the Geolocation Position or Error.",
+                "parameters": [
+                    { "name": "latitude", "type": "number", "optional": true, "description": "Mock longitude"},
+                    { "name": "longitude", "type": "number", "optional": true, "description": "Mock latitude"},
+                    { "name": "accuracy", "type": "number", "optional": true, "description": "Mock accuracy"}
+                ]
+            },
+            {
+                "name": "clearGeolocationOverride",
+                "description": "Clears the overriden Geolocation Position and Error."
+            },
+            {
+                "name": "setDeviceOrientationOverride",
+                "description": "Overrides the Device Orientation.",
+                "parameters": [
+                    { "name": "alpha", "type": "number", "description": "Mock alpha"},
+                    { "name": "beta", "type": "number", "description": "Mock beta"},
+                    { "name": "gamma", "type": "number", "description": "Mock gamma"}
+                ],
+                "hidden": true
+            },
+            {
+                "name": "clearDeviceOrientationOverride",
+                "description": "Clears the overridden Device Orientation.",
+                "hidden": true
+            },
+            {
+                "name": "setTouchEmulationEnabled",
+                "parameters": [
+                    { "name": "enabled", "type": "boolean", "description": "Whether the touch event emulation should be enabled." }
+                ],
+                "description": "Toggles mouse event-based touch event emulation.",
+                "hidden": true
+            },
+            {
+                "name": "setEmulatedMedia",
+                "parameters": [
+                    { "name": "media", "type": "string", "description": "Media type to emulate. Empty string disables the override." }
+                ],
+                "description": "Emulates the given media for CSS media queries.",
+                "hidden": true
+            },
+            {
+                "name": "getCompositingBordersVisible",
+                "description": "Indicates the visibility of compositing borders.",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "If true, compositing borders are visible." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "setCompositingBordersVisible",
+                "description": "Controls the visibility of compositing borders.",
+                "parameters": [
+                    { "name": "visible", "type": "boolean", "description": "True for showing compositing borders." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "captureScreenshot",
+                "description": "Capture page screenshot.",
+                "returns": [
+                    { "name": "data", "type": "string", "description": "Base64-encoded image data (PNG)." }
+                ]
+            },
+            {
+                "name": "handleJavaScriptDialog",
+                "description": "Accepts or dismisses a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload).",
+                "parameters": [
+                    { "name": "accept", "type": "boolean", "description": "Whether to accept or dismiss the dialog." },
+                    { "name": "promptText", "type": "string", "optional": "true", "description": "The text to enter into the dialog prompt before accepting. Used only if this is a prompt dialog." }
+                ],
+                "hidden": true
+            }
+        ],
+        "events": [
+            {
+                "name": "domContentEventFired",
+                "parameters": [
+                    { "name": "timestamp", "type": "number" }
+                ]
+            },
+            {
+                "name": "loadEventFired",
+                "parameters": [
+                    { "name": "timestamp", "type": "number" }
+                ]
+            },
+            {
+                "name": "frameNavigated",
+                "description": "Fired once navigation of the frame has completed. Frame is now associated with the new loader.",
+                "parameters": [
+                    { "name": "frame", "$ref": "Frame", "description": "Frame object." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "frameDetached",
+                "description": "Fired when frame has been detached from its parent.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Id of the frame that has been detached." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "frameStartedLoading",
+                "description": "Fired when frame has started loading.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Id of the frame that has started loading." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "frameStoppedLoading",
+                "description": "Fired when frame has stopped loading.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Id of the frame that has stopped loading." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "frameScheduledNavigation",
+                "description": "Fired when frame schedules a potential navigation.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Id of the frame that has scheduled a navigation." },
+                    { "name": "delay", "type": "number", "description": "Delay (in seconds) until the navigation is scheduled to begin. The navigation is not guaranteed to start." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "frameClearedScheduledNavigation",
+                "description": "Fired when frame no longer has a scheduled navigation.",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Id of the frame that has cleared its scheduled navigation." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "javascriptDialogOpening",
+                "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) is about to open.",
+                "parameters": [
+                    { "name": "message", "type": "string", "description": "Message that will be displayed by the dialog." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "javascriptDialogClosed",
+                "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) has been closed.",
+                "hidden": true
+            },
+            {
+                "name": "scriptsEnabled",
+                "description": "Fired when the JavaScript is enabled/disabled on the page",
+                "parameters": [
+                    { "name": "isEnabled", "type": "boolean", "description": "Whether script execution is enabled or disabled on the page." }
+                ],
+                "hidden": true
+            }
+        ]
+    },
+    {
+        "domain": "Runtime",
+        "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.",
+        "types": [
+            {
+                "id": "RemoteObjectId",
+                "type": "string",
+                "description": "Unique object identifier."
+            },
+            {
+                "id": "RemoteObject",
+                "type": "object",
+                "description": "Mirror object referencing original JavaScript object.",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type." },
+                    { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." },
+                    { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." },
+                    { "name": "value", "type": "any", "optional": true, "description": "Remote object value (in case of primitive values or JSON values if it was requested)." },
+                    { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." },
+                    { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." },
+                    { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containsing abbreviated property values.", "hidden": true }
+                ]
+            },
+            {
+                "id": "ObjectPreview",
+                "type": "object",
+                "hidden": true,
+                "description": "Object containing abbreviated remote object value.",
+                "properties": [
+                    { "name": "lossless", "type": "boolean", "description": "Determines whether preview is lossless (contains all information of the original object)." },
+                    { "name": "overflow", "type": "boolean", "description": "True iff some of the properties of the original did not fit." },
+                    { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." }
+                ]
+            },
+            {
+                "id": "PropertyPreview",
+                "type": "object",
+                "hidden": true,
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Property name." },
+	                { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type." },
+	                { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." },
+	                { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." },
+	                { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }
+                ]
+            },
+            {
+                "id": "PropertyDescriptor",
+                "type": "object",
+                "description": "Object property descriptor.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Property name." },
+                    { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." },
+                    { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." },
+                    { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." },
+                    { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." },
+                    { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." },
+                    { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." },
+                    { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object.", "hidden": true }
+
+                ]
+            },
+            {
+                "id": "InternalPropertyDescriptor",
+                "type": "object",
+                "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Conventional property name." },
+                    { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }
+                ],
+                "hidden": true
+            },
+            {
+                "id": "CallArgument",
+                "type": "object",
+                "description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.",
+                "properties": [
+                    { "name": "value", "type": "any", "optional": true, "description": "Primitive value." },
+                    { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." }
+                ]
+            },
+            {
+                "id": "ExecutionContextId",
+                "type": "integer",
+                "description": "Id of an execution context."
+            },
+            {
+                "id": "ExecutionContextDescription",
+                "type": "object",
+                "description": "Description of an isolated world.",
+                "properties": [
+                    { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." },
+                    { "name": "isPageContext", "type": "boolean", "description": "True if this is a context where inpspected web page scripts run. False if it is a content script isolated context.", "hidden": true },
+                    { "name": "name", "type": "string", "description": "Human readable name describing given context.", "hidden": true},
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Id of the owning frame." }
+                ]
+            }
+
+        ],
+        "commands": [
+            {
+                "name": "evaluate",
+                "parameters": [
+                    { "name": "expression", "type": "string", "description": "Expression to evaluate." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." },
+                    { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true },
+                    { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true },
+                    { "name": "contextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform evaluation. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." },
+                    { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." },
+                    { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ],
+                "description": "Evaluates expression on global object."
+            },
+            {
+                "name": "callFunctionOn",
+                "parameters": [
+                    { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." },
+                    { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." },
+                    { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." },
+                    { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true },
+                    { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." },
+                    { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "RemoteObject", "description": "Call result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ],
+                "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object."
+            },
+            {
+                "name": "getProperties",
+                "parameters": [
+                    { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." },
+                    { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor"}, "description": "Object properties." },
+                    { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor"}, "description": "Internal object properties.", "hidden": true }
+                ],
+                "description": "Returns properties of a given object. Object group of the result is inherited from the target object."
+            },
+            {
+                "name": "releaseObject",
+                "parameters": [
+                    { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." }
+                ],
+                "description": "Releases remote object with given id."
+            },
+            {
+                "name": "releaseObjectGroup",
+                "parameters": [
+                    { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." }
+                ],
+                "description": "Releases all remote objects that belong to a given group."
+            },
+            {
+                "name": "run",
+                "hidden": true,
+                "description": "Tells inspected instance(worker or page) that it can run in case it was started paused."
+            },
+            {
+                "name": "enable",
+                "description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context."
+            },
+            {
+                "name": "disable",
+                "hidden": true,
+                "description": "Disables reporting of execution contexts creation."
+            }
+        ],
+        "events": [
+            {
+                "name": "executionContextCreated",
+                "parameters": [
+                    { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." }
+                ],
+                "description": "Issued when new execution context is created."
+            }
+        ]
+    },
+    {
+        "domain": "Console",
+        "description": "Console domain defines methods and events for interaction with the JavaScript console. Console collects messages created by means of the <a href='http://getfirebug.com/wiki/index.php/Console_API'>JavaScript Console API</a>. One needs to enable this domain using <code>enable</code> command in order to start receiving the console messages. Browser collects messages issued while console domain is not enabled as well and reports them using <code>messageAdded</code> notification upon enabling.",
+        "types": [
+            {
+                "id": "ConsoleMessage",
+                "type": "object",
+                "description": "Console message.",
+                "properties": [
+                    { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "css", "security", "other", "deprecation"], "description": "Message source." },
+                    { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug"], "description": "Message severity." },
+                    { "name": "text", "type": "string", "description": "Message text." },
+                    { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "timing", "profile", "profileEnd"], "description": "Console message type." },
+                    { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." },
+                    { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." },
+                    { "name": "repeatCount", "type": "integer", "optional": true, "description": "Repeat count for repeated messages." },
+                    { "name": "parameters", "type": "array", "items": { "$ref": "Runtime.RemoteObject" }, "optional": true, "description": "Message parameters in case of the formatted message." },
+                    { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." },
+                    { "name": "networkRequestId", "$ref": "Network.RequestId", "optional": true, "description": "Identifier of the network request associated with this message." }
+                ]
+            },
+            {
+                "id": "CallFrame",
+                "type": "object",
+                "description": "Stack entry for console errors and assertions.",
+                "properties": [
+                    { "name": "functionName", "type": "string", "description": "JavaScript function name." },
+                    { "name": "url", "type": "string", "description": "JavaScript script name or url." },
+                    { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number." },
+                    { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number." }
+                ]
+            },
+            {
+                "id": "StackTrace",
+                "type": "array",
+                "items": { "$ref": "CallFrame" },
+                "description": "Call frames for assertions or error messages."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification."
+            },
+            {
+                "name": "disable",
+                "description": "Disables console domain, prevents further console messages from being reported to the client."
+            },
+            {
+                "name": "clearMessages",
+                "description": "Clears console messages collected in the browser."
+            },
+            {
+                "name": "setMonitoringXHREnabled",
+                "parameters": [
+                    { "name": "enabled", "type": "boolean", "description": "Monitoring enabled state." }
+                ],
+                "description": "Toggles monitoring of XMLHttpRequest. If <code>true</code>, console will receive messages upon each XHR issued.",
+                "hidden": true
+            },
+            {
+                "name": "addInspectedNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "DOM node id to be accessible by means of $x command line API." }
+                ],
+                "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions).",
+                "hidden": true
+            },
+            {
+                "name": "addInspectedHeapObject",
+                "parameters": [
+                    { "name": "heapObjectId", "type": "integer" }
+                ],
+                "hidden": true
+            }
+        ],
+        "events": [
+            {
+                "name": "messageAdded",
+                "parameters": [
+                    { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." }
+                ],
+                "description": "Issued when new console message is added."
+            },
+            {
+                "name": "messageRepeatCountUpdated",
+                "parameters": [
+                    { "name": "count", "type": "integer", "description": "New repeat count value." }
+                ],
+                "description": "Issued when subsequent message(s) are equal to the previous one(s)."
+            },
+            {
+                "name": "messagesCleared",
+                "description": "Issued when console is cleared. This happens either upon <code>clearMessages</code> command or after page navigation."
+            }
+        ]
+    },
+    {
+        "domain": "Network",
+        "description": "Network domain allows tracking network activities of the page. It exposes information about http, file, data and other requests and responses, their headers, bodies, timing, etc.",
+        "types": [
+            {
+                "id": "LoaderId",
+                "type": "string",
+                "description": "Unique loader identifier."
+            },
+            {
+                "id": "FrameId",
+                "type": "string",
+                "description": "Unique frame identifier.",
+                "hidden": true
+            },
+            {
+                "id": "RequestId",
+                "type": "string",
+                "description": "Unique request identifier."
+            },
+            {
+                "id": "Timestamp",
+                "type": "number",
+                "description": "Number of seconds since epoch."
+            },
+            {
+                "id": "Headers",
+                "type": "object",
+                "description": "Request / response headers as keys / values of JSON object."
+            },
+            {
+                "id": "ResourceTiming",
+                "type": "object",
+                "description": "Timing information for the request.",
+                "properties": [
+                    { "name": "requestTime", "type": "number", "description": "Timing's requestTime is a baseline in seconds, while the other numbers are ticks in milliseconds relatively to this requestTime." },
+                    { "name": "proxyStart", "type": "number", "description": "Started resolving proxy." },
+                    { "name": "proxyEnd", "type": "number", "description": "Finished resolving proxy." },
+                    { "name": "dnsStart", "type": "number", "description": "Started DNS address resolve." },
+                    { "name": "dnsEnd", "type": "number", "description": "Finished DNS address resolve." },
+                    { "name": "connectStart", "type": "number", "description": "Started connecting to the remote host." },
+                    { "name": "connectEnd", "type": "number", "description": "Connected to the remote host." },
+                    { "name": "sslStart", "type": "number", "description": "Started SSL handshake." },
+                    { "name": "sslEnd", "type": "number", "description": "Finished SSL handshake." },
+                    { "name": "sendStart", "type": "number", "description": "Started sending request." },
+                    { "name": "sendEnd", "type": "number", "description": "Finished sending request." },
+                    { "name": "receiveHeadersEnd", "type": "number", "description": "Finished receiving response headers." }
+                ]
+            },
+            {
+                "id": "Request",
+                "type": "object",
+                "description": "HTTP request data.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Request URL." },
+                    { "name": "method", "type": "string", "description": "HTTP request method." },
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP request headers." },
+                    { "name": "postData", "type": "string", "optional": true, "description": "HTTP POST request data." }
+                ]
+            },
+            {
+                "id": "Response",
+                "type": "object",
+                "description": "HTTP response data.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Response URL. This URL can be different from CachedResource.url in case of redirect." },
+                    { "name": "status", "type": "number", "description": "HTTP response status code." },
+                    { "name": "statusText", "type": "string", "description": "HTTP response status text." },
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." },
+                    { "name": "headersText", "type": "string", "optional": true, "description": "HTTP response headers text." },
+                    { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." },
+                    { "name": "requestHeaders", "$ref": "Headers", "optional": true, "description": "Refined HTTP request headers that were actually transmitted over the network." },
+                    { "name": "requestHeadersText", "type": "string", "optional": true, "description": "HTTP request headers text." },
+                    { "name": "connectionReused", "type": "boolean", "description": "Specifies whether physical connection was actually reused for this request." },
+                    { "name": "connectionId", "type": "number", "description": "Physical connection id that was actually used for this request." },
+                    { "name": "fromDiskCache", "type": "boolean", "optional": true, "description": "Specifies that the request was served from the disk cache." },
+                    { "name": "timing", "$ref": "ResourceTiming", "optional": true, "description": "Timing information for the given request." }
+                ]
+            },
+            {
+                "id": "WebSocketRequest",
+                "type": "object",
+                "description": "WebSocket request data.",
+                "hidden": true,
+                "properties": [
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." }
+                ]
+            },
+            {
+                "id": "WebSocketResponse",
+                "type": "object",
+                "description": "WebSocket response data.",
+                "hidden": true,
+                "properties": [
+                    { "name": "status", "type": "number", "description": "HTTP response status code." },
+                    { "name": "statusText", "type": "string", "description": "HTTP response status text." },
+                    { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." }
+                ]
+            },
+            {
+                "id": "WebSocketFrame",
+                "type": "object",
+                "description": "WebSocket frame data.",
+                "hidden": true,
+                "properties": [
+                    { "name": "opcode", "type": "number", "description": "WebSocket frame opcode." },
+                    { "name": "mask", "type": "boolean", "description": "WebSocke frame mask." },
+                    { "name": "payloadData", "type": "string", "description": "WebSocke frame payload data." }
+                ]
+            },
+            {
+                "id": "CachedResource",
+                "type": "object",
+                "description": "Information about the cached resource.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Resource URL. This is the url of the original network request." },
+                    { "name": "type", "$ref": "Page.ResourceType", "description": "Type of this resource." },
+                    { "name": "response", "$ref": "Response", "optional": true, "description": "Cached response data." },
+                    { "name": "bodySize", "type": "number", "description": "Cached response body size." }
+                ]
+            },
+            {
+                "id": "Initiator",
+                "type": "object",
+                "description": "Information about the request initiator.",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["parser", "script", "other"], "description": "Type of this initiator." },
+                    { "name": "stackTrace", "$ref": "Console.StackTrace", "optional": true, "description": "Initiator JavaScript stack trace, set for Script only." },
+                    { "name": "url", "type": "string", "optional": true, "description": "Initiator URL, set for Parser type only." },
+                    { "name": "lineNumber", "type": "number", "optional": true, "description": "Initiator line number, set for Parser type only." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables network tracking, network events will now be delivered to the client."
+            },
+            {
+                "name": "disable",
+                "description": "Disables network tracking, prevents network events from being sent to the client."
+            },
+            {
+                "name": "setUserAgentOverride",
+                "description": "Allows overriding user agent with the given string.",
+                "parameters": [
+                    { "name": "userAgent", "type": "string", "description": "User agent to use." }
+                ]
+            },
+            {
+                "name": "setExtraHTTPHeaders",
+                "description": "Specifies whether to always send extra HTTP headers with the requests from this page.",
+                "parameters": [
+                    { "name": "headers", "$ref": "Headers", "description": "Map with extra HTTP headers." }
+                ]
+            },
+            {
+                "name": "getResponseBody",
+                "description": "Returns content served for the given request.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Identifier of the network request to get content for." }
+                ],
+                "returns": [
+                    { "name": "body", "type": "string", "description": "Response body." },
+                    { "name": "base64Encoded", "type": "boolean", "description": "True, if content was sent as base64." }
+                ]
+            },
+            {
+                "name": "replayXHR",
+                "description": "This method sends a new XMLHttpRequest which is identical to the original one. The following parameters should be identical: method, url, async, request body, extra headers, withCredentials attribute, user, password.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Identifier of XHR to replay." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "canClearBrowserCache",
+                "description": "Tells whether clearing browser cache is supported.",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "True if browser cache can be cleared." }
+                ]
+            },
+            {
+                "name": "clearBrowserCache",
+                "description": "Clears browser cache."
+            },
+            {
+                "name": "canClearBrowserCookies",
+                "description": "Tells whether clearing browser cookies is supported.",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "True if browser cookies can be cleared." }
+                ]
+            },
+            {
+                "name": "clearBrowserCookies",
+                "description": "Clears browser cookies."
+            },
+            {
+                "name": "setCacheDisabled",
+                "parameters": [
+                    { "name": "cacheDisabled", "type": "boolean", "description": "Cache disabled state." }
+                ],
+                "description": "Toggles ignoring cache for each request. If <code>true</code>, cache will not be used."
+            }
+        ],
+        "events": [
+            {
+                "name": "requestWillBeSent",
+                "description": "Fired when page is about to send HTTP request.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "frameId", "$ref": "FrameId", "description": "Frame identifier.", "hidden": true },
+                    { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." },
+                    { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." },
+                    { "name": "request", "$ref": "Request", "description": "Request data." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." },
+                    { "name": "redirectResponse", "optional": true, "$ref": "Response", "description": "Redirect response data." }
+                ]
+            },
+            {
+                "name": "requestServedFromCache",
+                "description": "Fired if request ended up loading from cache.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }
+                ]
+            },
+            {
+                "name": "responseReceived",
+                "description": "Fired when HTTP response is available.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "frameId", "$ref": "FrameId", "description": "Frame identifier.", "hidden": true },
+                    { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "type", "$ref": "Page.ResourceType", "description": "Resource type." },
+                    { "name": "response", "$ref": "Response", "description": "Response data." }
+                ]
+            },
+            {
+                "name": "dataReceived",
+                "description": "Fired when data chunk was received over the network.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "dataLength", "type": "integer", "description": "Data chunk length." },
+                    { "name": "encodedDataLength", "type": "integer", "description": "Actual bytes received (might be less than dataLength for compressed encodings)." }
+                ]
+            },
+            {
+                "name": "loadingFinished",
+                "description": "Fired when HTTP request has finished loading.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }
+                ]
+            },
+            {
+                "name": "loadingFailed",
+                "description": "Fired when HTTP request has failed to load.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "errorText", "type": "string", "description": "User friendly error message." },
+                    { "name": "canceled", "type": "boolean", "optional": true, "description": "True if loading was canceled." }
+                ]
+            },
+            {
+                "name": "requestServedFromMemoryCache",
+                "description": "Fired when HTTP request has been served from memory cache.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "frameId", "$ref": "FrameId", "description": "Frame identifier.", "hidden": true },
+                    { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." },
+                    { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." },
+                    { "name": "resource", "$ref": "CachedResource", "description": "Cached resource data." }
+                ]
+            },
+            {
+                "name": "webSocketWillSendHandshakeRequest",
+                "description": "Fired when WebSocket is about to initiate handshake.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "request", "$ref": "WebSocketRequest", "description": "WebSocket request data." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketHandshakeResponseReceived",
+                "description": "Fired when WebSocket handshake response becomes available.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "response", "$ref": "WebSocketResponse", "description": "WebSocket response data." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketCreated",
+                "description": "Fired upon WebSocket creation.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "url", "type": "string", "description": "WebSocket request URL." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketClosed",
+                "description": "Fired when WebSocket is closed.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketFrameReceived",
+                "description": "Fired when WebSocket frame is received.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketFrameError",
+                "description": "Fired when WebSocket frame error occurs.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "errorMessage", "type": "string", "description": "WebSocket frame error message." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "webSocketFrameSent",
+                "description": "Fired when WebSocket frame is sent.",
+                "parameters": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." },
+                    { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." },
+                    { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." }
+                ],
+                "hidden": true
+            }
+        ]
+    },
+    {
+        "domain": "Database",
+        "hidden": true,
+        "types": [
+            {
+                "id": "DatabaseId",
+                "type": "string",
+                "description": "Unique identifier of Database object.",
+                "hidden": true
+            },
+            {
+                "id": "Database",
+                "type": "object",
+                "description": "Database object.",
+                "hidden": true,
+                "properties": [
+                    { "name": "id", "$ref": "DatabaseId", "description": "Database ID." },
+                    { "name": "domain", "type": "string", "description": "Database domain." },
+                    { "name": "name", "type": "string", "description": "Database name." },
+                    { "name": "version", "type": "string", "description": "Database version." }
+                ]
+            },
+            {
+                "id": "Error",
+                "type": "object",
+                "description": "Database error.",
+                "properties": [
+                    { "name": "message", "type": "string", "description": "Error message." },
+                    { "name": "code", "type": "integer", "description": "Error code." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables database tracking, database events will now be delivered to the client."
+            },
+            {
+                "name": "disable",
+                "description": "Disables database tracking, prevents database events from being sent to the client."
+            },
+            {
+                "name": "getDatabaseTableNames",
+                "parameters": [
+                    { "name": "databaseId", "$ref": "DatabaseId" }
+                ],
+                "returns": [
+                    { "name": "tableNames", "type": "array", "items": { "type": "string" } }
+                ]
+            },
+            {
+                "name": "executeSQL",
+                "async": true,
+                "parameters": [
+                    { "name": "databaseId", "$ref": "DatabaseId" },
+                    { "name": "query", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "columnNames", "type": "array", "optional": true, "items": { "type": "string" } },
+                    { "name": "values", "type": "array", "optional": true, "items": { "type": "any" }},
+                    { "name": "sqlError", "$ref": "Error", "optional": true }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "addDatabase",
+                "parameters": [
+                    { "name": "database", "$ref": "Database" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "IndexedDB",
+        "hidden": true,
+        "types": [
+            {
+                "id": "DatabaseWithObjectStores",
+                "type": "object",
+                "description": "Database with an array of object stores.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Database name." },
+                    { "name": "version", "type": "string", "description": "Deprecated string database version." },
+                    { "name": "intVersion", "type": "integer", "description": "Integer database version." },
+                    { "name": "objectStores", "type": "array", "items": { "$ref": "ObjectStore" }, "description": "Object stores in this database." }
+                ]
+            },
+            {
+                "id": "ObjectStore",
+                "type": "object",
+                "description": "Object store.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Object store name." },
+                    { "name": "keyPath", "$ref": "KeyPath", "description": "Object store key path." },
+                    { "name": "autoIncrement", "type": "boolean", "description": "If true, object store has auto increment flag set." },
+                    { "name": "indexes", "type": "array", "items": { "$ref": "ObjectStoreIndex" }, "description": "Indexes in this object store." }
+                ]
+            },
+            {
+                "id": "ObjectStoreIndex",
+                "type": "object",
+                "description": "Object store index.",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Index name." },
+                    { "name": "keyPath", "$ref": "KeyPath", "description": "Index key path." },
+                    { "name": "unique", "type": "boolean", "description": "If true, index is unique." },
+                    { "name": "multiEntry", "type": "boolean", "description": "If true, index allows multiple entries for a key." }
+                ]
+            },
+            {
+                "id": "Key",
+                "type": "object",
+                "description": "Key.",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["number", "string", "date", "array"], "description": "Key type." },
+                    { "name": "number", "type": "number", "optional": true, "description": "Number value." },
+                    { "name": "string", "type": "string", "optional": true, "description": "String value." },
+                    { "name": "date", "type": "number", "optional": true, "description": "Date value." },
+                    { "name": "array", "type": "array", "optional": true, "items": { "$ref": "Key" }, "description": "Array value." }
+                ]
+            },
+            {
+                "id": "KeyRange",
+                "type": "object",
+                "description": "Key range.",
+                "properties": [
+                    { "name": "lower", "$ref": "Key", "optional": true, "description": "Lower bound." },
+                    { "name": "upper", "$ref": "Key", "optional": true, "description": "Upper bound." },
+                    { "name": "lowerOpen", "type": "boolean", "description": "If true lower bound is open." },
+                    { "name": "upperOpen", "type": "boolean", "description": "If true upper bound is open." }
+                ]
+            },
+            {
+                "id": "DataEntry",
+                "type": "object",
+                "description": "Data entry.",
+                "properties": [
+                    { "name": "key", "$ref": "Runtime.RemoteObject", "description": "Key." },
+                    { "name": "primaryKey", "$ref": "Runtime.RemoteObject", "description": "Primary key." },
+                    { "name": "value", "$ref": "Runtime.RemoteObject", "description": "Value." }
+                ]
+            },
+            {
+                "id": "KeyPath",
+                "type": "object",
+                "description": "Key path.",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["null", "string", "array"], "description": "Key path type." },
+                    { "name": "string", "type": "string", "optional": true, "description": "String value." },
+                    { "name": "array", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Array value." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables events from backend."
+            },
+            {
+                "name": "disable",
+                "description": "Disables events from backend."
+            },
+            {
+                "name": "requestDatabaseNames",
+                "async": true,
+                "parameters": [
+                    { "name": "securityOrigin", "type": "string", "description": "Security origin." }
+                ],
+                "returns": [
+                    { "name": "databaseNames", "type": "array", "items": { "type": "string" }, "description": "Database names for origin." }
+                ],
+                "description": "Requests database names for given security origin."
+            },
+            {
+                "name": "requestDatabase",
+                "async": true,
+                "parameters": [
+                    { "name": "securityOrigin", "type": "string", "description": "Security origin." },
+                    { "name": "databaseName", "type": "string", "description": "Database name." }
+                ],
+                "returns": [
+                    { "name": "databaseWithObjectStores", "$ref": "DatabaseWithObjectStores", "description": "Database with an array of object stores." }
+                ],
+                "description": "Requests database with given name in given frame."
+            },
+            {
+                "name": "requestData",
+                "async": true,
+                "parameters": [
+                    { "name": "securityOrigin", "type": "string", "description": "Security origin." },
+                    { "name": "databaseName", "type": "string", "description": "Database name." },
+                    { "name": "objectStoreName", "type": "string", "description": "Object store name." },
+                    { "name": "indexName", "type": "string", "description": "Index name, empty string for object store data requests." },
+                    { "name": "skipCount", "type": "integer", "description": "Number of records to skip." },
+                    { "name": "pageSize", "type": "integer", "description": "Number of records to fetch." },
+                    { "name": "keyRange", "$ref": "KeyRange", "optional": true, "description": "Key range." }
+                ],
+                "returns": [
+                    { "name": "objectStoreDataEntries", "type": "array", "items": { "$ref": "DataEntry" }, "description": "Array of object store data entries." },
+                    { "name": "hasMore", "type": "boolean", "description": "If true, there are more entries to fetch in the given range." }
+                ],
+                "description": "Requests data from object store or index."
+            },
+            {
+                "name": "clearObjectStore",
+                "async": true,
+                "parameters": [
+                    { "name": "securityOrigin", "type": "string", "description": "Security origin." },
+                    { "name": "databaseName", "type": "string", "description": "Database name." },
+                    { "name": "objectStoreName", "type": "string", "description": "Object store name." }
+                ],
+                "returns": [
+                ],
+                "description": "Clears all entries from an object store."
+            }
+        ]
+    },
+    {
+        "domain": "DOMStorage",
+        "hidden": true,
+        "description": "Query and modify DOM storage.",
+        "types": [
+            {
+                "id": "StorageId",
+                "type": "object",
+                "description": "DOM Storage identifier.",
+                "hidden": true,
+                "properties": [
+                    { "name": "securityOrigin", "type": "string", "description": "Security origin for the storage." },
+                    { "name": "isLocalStorage", "type": "boolean", "description": "Whether the storage is local storage (not session storage)." }
+                ]
+            },
+            {
+                "id": "Item",
+                "type": "array",
+                "description": "DOM Storage item.",
+                "hidden": true,
+                "items": { "type": "string" }
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables storage tracking, storage events will now be delivered to the client."
+            },
+            {
+                "name": "disable",
+                "description": "Disables storage tracking, prevents storage events from being sent to the client."
+            },
+            {
+                "name": "getDOMStorageItems",
+                "parameters": [
+                    { "name": "storageId", "$ref": "StorageId" }
+                ],
+                "returns": [
+                    { "name": "entries", "type": "array", "items": { "$ref": "Item" } }
+                ]
+            },
+            {
+                "name": "setDOMStorageItem",
+                "parameters": [
+                    { "name": "storageId", "$ref": "StorageId" },
+                    { "name": "key", "type": "string" },
+                    { "name": "value", "type": "string" }
+                ]
+            },
+            {
+                "name": "removeDOMStorageItem",
+                "parameters": [
+                    { "name": "storageId", "$ref": "StorageId" },
+                    { "name": "key", "type": "string" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "domStorageItemsCleared",
+                "parameters": [
+                    { "name": "storageId",  "$ref": "StorageId" }
+                ]
+            },
+            {
+                "name": "domStorageItemRemoved",
+                "parameters": [
+                    { "name": "storageId",  "$ref": "StorageId" },
+                    { "name": "key", "type": "string" }
+                ]
+            },
+            {
+                "name": "domStorageItemAdded",
+                "parameters": [
+                    { "name": "storageId",  "$ref": "StorageId" },
+                    { "name": "key", "type": "string" },
+                    { "name": "newValue", "type": "string" }
+                ]
+            },
+            {
+                "name": "domStorageItemUpdated",
+                "parameters": [
+                    { "name": "storageId",  "$ref": "StorageId" },
+                    { "name": "key", "type": "string" },
+                    { "name": "oldValue", "type": "string" },
+                    { "name": "newValue", "type": "string" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "ApplicationCache",
+        "hidden": true,
+        "types": [
+            {
+                "id": "ApplicationCacheResource",
+                "type": "object",
+                "description": "Detailed application cache resource information.",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "Resource url." },
+                    { "name": "size", "type": "integer", "description": "Resource size." },
+                    { "name": "type", "type": "string", "description": "Resource type." }
+                ]
+            },
+            {
+                "id": "ApplicationCache",
+                "type": "object",
+                "description": "Detailed application cache information.",
+                "properties": [
+                    { "name": "manifestURL", "type": "string", "description": "Manifest URL." },
+                    { "name": "size", "type": "number", "description": "Application cache size." },
+                    { "name": "creationTime", "type": "number", "description": "Application cache creation time." },
+                    { "name": "updateTime", "type": "number", "description": "Application cache update time." },
+                    { "name": "resources", "type": "array", "items": { "$ref": "ApplicationCacheResource" }, "description": "Application cache resources." }
+                ]
+            },
+            {
+                "id": "FrameWithManifest",
+                "type": "object",
+                "description": "Frame identifier - manifest URL pair.",
+                "properties": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Frame identifier." },
+                    { "name": "manifestURL", "type": "string", "description": "Manifest URL." },
+                    { "name": "status", "type": "integer", "description": "Application cache status." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "getFramesWithManifests",
+                "returns": [
+                    { "name": "frameIds", "type": "array", "items": { "$ref": "FrameWithManifest" }, "description": "Array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." }
+                ],
+                "description": "Returns array of frame identifiers with manifest urls for each frame containing a document associated with some application cache."
+            },
+            {
+                "name": "enable",
+                "description": "Enables application cache domain notifications."
+            },
+            {
+                "name": "getManifestForFrame",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame containing document whose manifest is retrieved." }
+                ],
+                "returns": [
+                    { "name": "manifestURL", "type": "string", "description": "Manifest URL for document in the given frame." }
+                ],
+                "description": "Returns manifest URL for document in the given frame."
+            },
+            {
+                "name": "getApplicationCacheForFrame",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame containing document whose application cache is retrieved." }
+                ],
+                "returns": [
+                    { "name": "applicationCache", "$ref": "ApplicationCache", "description": "Relevant application cache data for the document in given frame." }
+                ],
+                "description": "Returns relevant application cache data for the document in given frame."
+            }
+        ],
+        "events": [
+            {
+                "name": "applicationCacheStatusUpdated",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame containing document whose application cache updated status." },
+                    { "name": "manifestURL", "type": "string", "description": "Manifest URL." },
+                    { "name": "status", "type": "integer", "description": "Updated application cache status." }
+                ]
+            },
+            {
+                "name": "networkStateUpdated",
+                "parameters": [
+                    { "name": "isNowOnline", "type": "boolean" }
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "FileSystem",
+        "hidden": true,
+        "types": [
+            {
+                "id": "Entry",
+                "type": "object",
+                "properties": [
+                    { "name": "url", "type": "string", "description": "filesystem: URL for the entry." },
+                    { "name": "name", "type": "string", "description": "The name of the file or directory." },
+                    { "name": "isDirectory", "type": "boolean", "description": "True if the entry is a directory." },
+                    { "name": "mimeType", "type": "string", "optional": true, "description": "MIME type of the entry, available for a file only." },
+                    { "name": "resourceType", "$ref": "Page.ResourceType", "optional": true, "description": "ResourceType of the entry, available for a file only." },
+                    { "name": "isTextFile", "type": "boolean", "optional": true, "description": "True if the entry is a text file." }
+                ],
+                "description": "Represents a browser side file or directory."
+            },
+            {
+                "id": "Metadata",
+                "type": "object",
+                "properties": [
+                    { "name": "modificationTime", "type": "number", "description": "Modification time." },
+                    { "name": "size", "type": "number", "description": "File size. This field is always zero for directories." }
+                ],
+                "description": "Represents metadata of a file or entry."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables events from backend."
+            },
+            {
+                "name": "disable",
+                "description": "Disables events from backend."
+            },
+            {
+                "name": "requestFileSystemRoot",
+                "async": true,
+                "parameters": [
+                    { "name": "origin", "type": "string", "description": "Security origin of requesting FileSystem. One of frames in current page needs to have this security origin." },
+                    { "name": "type", "type": "string", "enum": ["temporary", "persistent"], "description": "FileSystem type of requesting FileSystem." }
+                ],
+                "returns": [
+                    { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." },
+                    { "name": "root", "$ref": "Entry", "optional": true, "description": "Contains root of the requested FileSystem if the command completed successfully." }
+                ],
+                "description": "Returns root directory of the FileSystem, if exists."
+            },
+            {
+                "name": "requestDirectoryContent",
+                "async": true,
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL of the directory that the frontend is requesting to read from." }
+                ],
+                "returns": [
+                    { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." },
+                    { "name": "entries", "type": "array", "items": { "$ref": "Entry" }, "optional": true, "description": "Contains all entries on directory if the command completed successfully." }
+                ],
+                "description": "Returns content of the directory."
+            },
+            {
+                "name": "requestMetadata",
+                "async": true,
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL of the entry that the frontend is requesting to get metadata from." }
+                ],
+                "returns": [
+                    { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." },
+                    { "name": "metadata", "$ref": "Metadata", "optional": true, "description": "Contains metadata of the entry if the command completed successfully." }
+                ],
+                "description": "Returns metadata of the entry."
+            },
+            {
+                "name": "requestFileContent",
+                "async": true,
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL of the file that the frontend is requesting to read from." },
+                    { "name": "readAsText", "type": "boolean", "description": "True if the content should be read as text, otherwise the result will be returned as base64 encoded text." },
+                    { "name": "start", "type": "integer", "optional": true, "description": "Specifies the start of range to read." },
+                    { "name": "end", "type": "integer", "optional": true, "description": "Specifies the end of range to read exclusively." },
+                    { "name": "charset", "type": "string", "optional": true, "description": "Overrides charset of the content when content is served as text." }
+                ],
+                "returns": [
+                    { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." },
+                    { "name": "content", "type": "string", "optional": true, "description": "Content of the file." },
+                    { "name": "charset", "type": "string", "optional": true, "description": "Charset of the content if it is served as text." }
+                ],
+                "description": "Returns content of the file. Result should be sliced into [start, end)."
+            },
+            {
+                "name": "deleteEntry",
+                "async": true,
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL of the entry to delete." }
+                ],
+                "returns": [
+                    { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise errorCode is set to FileError::ErrorCode value." }
+                ],
+                "description": "Deletes specified entry. If the entry is a directory, the agent deletes children recursively."
+            }
+        ]
+    },
+    {
+        "domain": "DOM",
+        "description": "This domain exposes DOM read/write operations. Each DOM Node is represented with its mirror object that has an <code>id</code>. This <code>id</code> can be used to get additional information on the Node, resolve it into the JavaScript object wrapper, etc. It is important that client receives DOM events only for the nodes that are known to the client. Backend keeps track of the nodes that were sent to the client and never sends the same node twice. It is client's responsibility to collect information about the nodes that were sent to the client.<p>Note that <code>iframe</code> owner elements will return corresponding document elements as their child nodes.</p>",
+        "types": [
+            {
+                "id": "NodeId",
+                "type": "integer",
+                "description": "Unique DOM node identifier."
+            },
+            {
+                "id": "BackendNodeId",
+                "type": "integer",
+                "description": "Unique DOM node identifier used to reference a node that may not have been pushed to the front-end.",
+                "hidden": true
+            },
+            {
+                "id": "Node",
+                "type": "object",
+                "properties": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Node identifier that is passed into the rest of the DOM messages as the <code>nodeId</code>. Backend will only push node with given <code>id</code> once. It is aware of all requested nodes and will only fire DOM events for nodes known to the client." },
+                    { "name": "nodeType", "type": "integer", "description": "<code>Node</code>'s nodeType." },
+                    { "name": "nodeName", "type": "string", "description": "<code>Node</code>'s nodeName." },
+                    { "name": "localName", "type": "string", "description": "<code>Node</code>'s localName." },
+                    { "name": "nodeValue", "type": "string", "description": "<code>Node</code>'s nodeValue." },
+                    { "name": "childNodeCount", "type": "integer", "optional": true, "description": "Child count for <code>Container</code> nodes." },
+                    { "name": "children", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Child nodes of this node when requested with children." },
+                    { "name": "attributes", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Attributes of the <code>Element</code> node in the form of flat array <code>[name1, value1, name2, value2]</code>." },
+                    { "name": "documentURL", "type": "string", "optional": true, "description": "Document URL that <code>Document</code> or <code>FrameOwner</code> node points to." },
+                    { "name": "baseURL", "type": "string", "optional": true, "description": "Base URL that <code>Document</code> or <code>FrameOwner</code> node uses for URL completion.", "hidden": true },
+                    { "name": "publicId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s publicId." },
+                    { "name": "systemId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s systemId." },
+                    { "name": "internalSubset", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s internalSubset." },
+                    { "name": "xmlVersion", "type": "string", "optional": true, "description": "<code>Document</code>'s XML version in case of XML documents." },
+                    { "name": "name", "type": "string", "optional": true, "description": "<code>Attr</code>'s name." },
+                    { "name": "value", "type": "string", "optional": true, "description": "<code>Attr</code>'s value." },
+                    { "name": "frameId", "$ref": "Network.FrameId", "optional": true, "description": "Frame ID for frame owner elements.", "hidden": true },
+                    { "name": "contentDocument", "$ref": "Node", "optional": true, "description": "Content document for frame owner elements." },
+                    { "name": "shadowRoots", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Shadow root list for given element host.", "hidden": true },
+                    { "name": "templateContent", "$ref": "Node", "optional": true, "description": "Content document fragment for template elements", "hidden": true }
+                ],
+                "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type."
+            },
+            {
+                "id": "EventListener",
+                "type": "object",
+                "hidden": true,
+                "properties": [
+                    { "name": "type", "type": "string", "description": "<code>EventListener</code>'s type." },
+                    { "name": "useCapture", "type": "boolean", "description": "<code>EventListener</code>'s useCapture." },
+                    { "name": "isAttribute", "type": "boolean", "description": "<code>EventListener</code>'s isAttribute." },
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Target <code>DOMNode</code> id." },
+                    { "name": "handlerBody", "type": "string", "description": "Event handler function body." },
+                    { "name": "location", "$ref": "Debugger.Location", "optional": true, "description": "Handler code location." },
+                    { "name": "sourceName", "type": "string", "optional": true, "description": "Source script URL." },
+                    { "name": "handler", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Event handler function value." }
+                ],
+                "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type."
+            },
+            {
+                "id": "RGBA",
+                "type": "object",
+                "properties": [
+                    { "name": "r", "type": "integer", "description": "The red component, in the [0-255] range." },
+                    { "name": "g", "type": "integer", "description": "The green component, in the [0-255] range." },
+                    { "name": "b", "type": "integer", "description": "The blue component, in the [0-255] range." },
+                    { "name": "a", "type": "number", "optional": true, "description": "The alpha component, in the [0-1] range (default: 1)." }
+                ],
+                "description": "A structure holding an RGBA color."
+            },
+            {
+                "id": "Quad",
+                "type": "array",
+                "items": { "type": "number" },
+                "minItems": 8,
+                "maxItems": 8,
+                "description": "An array of quad vertices, x immediately followed by y for each point, points clock-wise."
+            },
+            {
+                "id": "HighlightConfig",
+                "type": "object",
+                "properties": [
+                    { "name": "showInfo", "type": "boolean", "optional": true, "description": "Whether the node info tooltip should be shown (default: false)." },
+                    { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." },
+                    { "name": "paddingColor", "$ref": "RGBA", "optional": true, "description": "The padding highlight fill color (default: transparent)." },
+                    { "name": "borderColor", "$ref": "RGBA", "optional": true, "description": "The border highlight fill color (default: transparent)." },
+                    { "name": "marginColor", "$ref": "RGBA", "optional": true, "description": "The margin highlight fill color (default: transparent)." },
+                    { "name": "eventTargetColor", "$ref": "RGBA", "optional": true, "hidden": true, "description": "The event target element highlight fill color (default: transparent)." }
+                ],
+                "description": "Configuration data for the highlighting of page elements."
+            }
+        ],
+        "commands": [
+            {
+                "name": "getDocument",
+                "returns": [
+                    { "name": "root", "$ref": "Node", "description": "Resulting node." }
+                ],
+                "description": "Returns the root DOM node to the caller."
+            },
+            {
+                "name": "requestChildNodes",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get children for." },
+                    { "name": "depth", "type": "integer", "optional": true, "description": "The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the entire subtree or provide an integer larger than 0.", "hidden": true }
+                ],
+                "description": "Requests that children of the node with given id are returned to the caller in form of <code>setChildNodes</code> events where not only immediate children are retrieved, but all children down to the specified depth."
+            },
+            {
+                "name": "querySelector",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." },
+                    { "name": "selector", "type": "string", "description": "Selector string." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Query selector result." }
+                ],
+                "description": "Executes <code>querySelector</code> on a given node."
+            },
+            {
+                "name": "querySelectorAll",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." },
+                    { "name": "selector", "type": "string", "description": "Selector string." }
+                ],
+                "returns": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Query selector result." }
+                ],
+                "description": "Executes <code>querySelectorAll</code> on a given node."
+            },
+            {
+                "name": "setNodeName",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set name for." },
+                    { "name": "name", "type": "string", "description": "New node's name." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "New node's id." }
+                ],
+                "description": "Sets node name for a node with given id."
+            },
+            {
+                "name": "setNodeValue",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set value for." },
+                    { "name": "value", "type": "string", "description": "New node's value." }
+                ],
+                "description": "Sets node value for a node with given id."
+            },
+            {
+                "name": "removeNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to remove." }
+                ],
+                "description": "Removes node with given id."
+            },
+            {
+                "name": "setAttributeValue",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attribute for." },
+                    { "name": "name", "type": "string", "description": "Attribute name." },
+                    { "name": "value", "type": "string", "description": "Attribute value." }
+                ],
+                "description": "Sets attribute for an element with given id."
+            },
+            {
+                "name": "setAttributesAsText",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attributes for." },
+                    { "name": "text", "type": "string", "description": "Text with a number of attributes. Will parse this text using HTML parser." },
+                    { "name": "name", "type": "string", "optional": true, "description": "Attribute name to replace with new attributes derived from text in case text parsed successfully." }
+                ],
+                "description": "Sets attributes on element with given id. This method is useful when user edits some existing attribute value and types in several attribute name/value pairs."
+            },
+            {
+                "name": "removeAttribute",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to remove attribute from." },
+                    { "name": "name", "type": "string", "description": "Name of the attribute to remove." }
+                ],
+                "description": "Removes attribute with given name from an element with given id."
+            },
+            {
+                "name": "getEventListenersForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get listeners for." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name for handler value. Handler value is not returned without this parameter specified." }
+                ],
+                "returns": [
+                    { "name": "listeners", "type": "array", "items": { "$ref": "EventListener"}, "description": "Array of relevant listeners." }
+                ],
+                "description": "Returns event listeners relevant to the node.",
+                "hidden": true
+            },
+            {
+                "name": "getOuterHTML",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get markup for." }
+                ],
+                "returns": [
+                    { "name": "outerHTML", "type": "string", "description": "Outer HTML markup." }
+                ],
+                "description": "Returns node's HTML markup."
+            },
+            {
+                "name": "setOuterHTML",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set markup for." },
+                    { "name": "outerHTML", "type": "string", "description": "Outer HTML markup to set." }
+                ],
+                "description": "Sets node HTML markup, returns new node id."
+            },
+            {
+                "name": "performSearch",
+                "parameters": [
+                    { "name": "query", "type": "string", "description": "Plain text or query selector or XPath search query." }
+                ],
+                "returns": [
+                    { "name": "searchId", "type": "string", "description": "Unique search session identifier." },
+                    { "name": "resultCount", "type": "integer", "description": "Number of search results." }
+                ],
+                "description": "Searches for a given string in the DOM tree. Use <code>getSearchResults</code> to access search results or <code>cancelSearch</code> to end this search session.",
+                "hidden": true
+            },
+            {
+                "name": "getSearchResults",
+                "parameters": [
+                    { "name": "searchId", "type": "string", "description": "Unique search session identifier." },
+                    { "name": "fromIndex", "type": "integer", "description": "Start index of the search result to be returned." },
+                    { "name": "toIndex", "type": "integer", "description": "End index of the search result to be returned." }
+                ],
+                "returns": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the search result nodes." }
+                ],
+                "description": "Returns search results from given <code>fromIndex</code> to given <code>toIndex</code> from the sarch with the given identifier.",
+                "hidden": true
+            },
+            {
+                "name": "discardSearchResults",
+                "parameters": [
+                    { "name": "searchId", "type": "string", "description": "Unique search session identifier." }
+                ],
+                "description": "Discards search results from the session with the given id. <code>getSearchResults</code> should no longer be called for that search.",
+                "hidden": true
+            },
+            {
+                "name": "requestNode",
+                "parameters": [
+                    { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "JavaScript object id to convert into node." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Node id for given object." }
+                ],
+                "description": "Requests that the node is sent to the caller given the JavaScript node object reference. All nodes that form the path from the node to the root are also sent to the client as a series of <code>setChildNodes</code> notifications."
+            },
+            {
+                "name": "setInspectModeEnabled",
+                "hidden": true,
+                "parameters": [
+                    { "name": "enabled", "type": "boolean", "description": "True to enable inspection mode, false to disable it." },
+                    { "name": "highlightConfig", "$ref": "HighlightConfig", "optional": true, "description": "A descriptor for the highlight appearance of hovered-over nodes. May be omitted if <code>enabled == false</code>." }
+                ],
+                "description": "Enters the 'inspect' mode. In this mode, elements that user is hovering over are highlighted. Backend then generates 'inspectNodeRequested' event upon element selection."
+            },
+            {
+                "name": "highlightRect",
+                "parameters": [
+                    { "name": "x", "type": "integer", "description": "X coordinate" },
+                    { "name": "y", "type": "integer", "description": "Y coordinate" },
+                    { "name": "width", "type": "integer", "description": "Rectangle width" },
+                    { "name": "height", "type": "integer", "description": "Rectangle height" },
+                    { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." },
+                    { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." }
+                ],
+                "description": "Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport."
+            },
+            {
+                "name": "highlightQuad",
+                "parameters": [
+                    { "name": "quad", "$ref": "Quad", "description": "Quad to highlight" },
+                    { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." },
+                    { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." }
+                ],
+                "description": "Highlights given quad. Coordinates are absolute with respect to the main frame viewport."
+            },
+            {
+                "name": "highlightNode",
+                "parameters": [
+                    { "name": "highlightConfig", "$ref": "HighlightConfig",  "description": "A descriptor for the highlight appearance." },
+                    { "name": "nodeId", "$ref": "NodeId", "optional": true, "description": "Identifier of the node to highlight." },
+                    { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "optional": true, "description": "JavaScript object id of the node to be highlighted.", "hidden": true }
+                ],
+                "description": "Highlights DOM node with given id or with the given JavaScript object wrapper. Either nodeId or objectId must be specified."
+            },
+            {
+                "name": "hideHighlight",
+                "description": "Hides DOM node highlight."
+            },
+            {
+                "name": "highlightFrame",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame to highlight." },
+                    { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." },
+                    { "name": "contentOutlineColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight outline color (default: transparent)." }
+                ],
+                "description": "Highlights owner element of the frame with given id.",
+                "hidden": true
+            },
+            {
+                "name": "pushNodeByPathToFrontend",
+                "parameters": [
+                    { "name": "path", "type": "string", "description": "Path to node in the proprietary format." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node for given path." }
+                ],
+                "description": "Requests that the node is sent to the caller given its path. // FIXME, use XPath",
+                "hidden": true
+            },
+            {
+                "name": "pushNodeByBackendIdToFrontend",
+                "parameters": [
+                    { "name": "backendNodeId", "$ref": "BackendNodeId", "description": "The backend node id of the node." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "The pushed node's id." }
+                ],
+                "description": "Requests that the node is sent to the caller given its backend node id.",
+                "hidden": true
+            },
+            {
+                "name": "releaseBackendNodeIds",
+                "parameters": [
+                    { "name": "nodeGroup", "type": "string", "description": "The backend node ids group name." }
+                ],
+                "description": "Requests that group of <code>BackendNodeIds</code> is released.",
+                "hidden": true
+            },
+            {
+                "name": "resolveNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to resolve." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }
+                ],
+                "returns": [
+                    { "name": "object", "$ref": "Runtime.RemoteObject", "description": "JavaScript object wrapper for given node." }
+                ],
+                "description": "Resolves JavaScript node object for given node id."
+            },
+            {
+                "name": "getAttributes",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to retrieve attibutes for." }
+                ],
+                "returns": [
+                    { "name": "attributes", "type": "array", "items": { "type": "string" }, "description": "An interleaved array of node attribute names and values." }
+                ],
+                "description": "Returns attributes for the specified node."
+            },
+            {
+                "name": "moveTo",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to drop." },
+                    { "name": "targetNodeId", "$ref": "NodeId", "description": "Id of the element to drop into." },
+                    { "name": "insertBeforeNodeId", "$ref": "NodeId", "optional": true, "description": "Drop node before given one." }
+                ],
+                "returns": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "New id of the moved node." }
+                ],
+                "description": "Moves node into the new container, places it before the given anchor."
+            },
+            {
+                "name": "undo",
+                "description": "Undoes the last performed action.",
+                "hidden": true
+            },
+            {
+                "name": "redo",
+                "description": "Re-does the last undone action.",
+                "hidden": true
+            },
+            {
+                "name": "markUndoableState",
+                "description": "Marks last undoable state.",
+                "hidden": true
+            },
+            {
+                "name": "focus",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to focus." }
+                ],
+                "description": "Focuses the given element.",
+                "hidden": true
+            },
+            {
+                "name": "setFileInputFiles",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the file input node to set files for." },
+                    { "name": "files", "type": "array", "items": { "type": "string" }, "description": "Array of file paths to set." }
+                ],
+                "description": "Sets files for the given file input element.",
+                "hidden": true
+            }
+        ],
+        "events": [
+            {
+                "name": "documentUpdated",
+                "description": "Fired when <code>Document</code> has been totally updated. Node ids are no longer valid."
+            },
+            {
+                "name": "inspectNodeRequested",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to inspect." }
+                ],
+                "description": "Fired when the node should be inspected. This happens after call to <code>setInspectModeEnabled</code>."
+            },
+            {
+                "name": "setChildNodes",
+                "parameters": [
+                    { "name": "parentId", "$ref": "NodeId", "description": "Parent node id to populate with children." },
+                    { "name": "nodes", "type": "array", "items": { "$ref": "Node"}, "description": "Child nodes array." }
+                ],
+                "description": "Fired when backend wants to provide client with the missing DOM structure. This happens upon most of the calls requesting node ids."
+            },
+            {
+                "name": "attributeModified",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "name", "type": "string", "description": "Attribute name." },
+                    { "name": "value", "type": "string", "description": "Attribute value." }
+                ],
+                "description": "Fired when <code>Element</code>'s attribute is modified."
+            },
+            {
+                "name": "attributeRemoved",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "name", "type": "string", "description": "A ttribute name." }
+                ],
+                "description": "Fired when <code>Element</code>'s attribute is removed."
+            },
+            {
+                "name": "inlineStyleInvalidated",
+                "parameters": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the nodes for which the inline styles have been invalidated." }
+                ],
+                "description": "Fired when <code>Element</code>'s inline style is modified via a CSS property modification.",
+                "hidden": true
+            },
+            {
+                "name": "characterDataModified",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "characterData", "type": "string", "description": "New text value." }
+                ],
+                "description": "Mirrors <code>DOMCharacterDataModified</code> event."
+            },
+            {
+                "name": "childNodeCountUpdated",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "childNodeCount", "type": "integer", "description": "New node count." }
+                ],
+                "description": "Fired when <code>Container</code>'s child node count has changed."
+            },
+            {
+                "name": "childNodeInserted",
+                "parameters": [
+                    { "name": "parentNodeId", "$ref": "NodeId", "description": "Id of the node that has changed." },
+                    { "name": "previousNodeId", "$ref": "NodeId", "description": "If of the previous siblint." },
+                    { "name": "node", "$ref": "Node", "description": "Inserted node data." }
+                ],
+                "description": "Mirrors <code>DOMNodeInserted</code> event."
+            },
+            {
+                "name": "childNodeRemoved",
+                "parameters": [
+                    { "name": "parentNodeId", "$ref": "NodeId", "description": "Parent id." },
+                    { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has been removed." }
+                ],
+                "description": "Mirrors <code>DOMNodeRemoved</code> event."
+            },
+            {
+                "name": "shadowRootPushed",
+                "parameters": [
+                    { "name": "hostId", "$ref": "NodeId", "description": "Host element id." },
+                    { "name": "root", "$ref": "Node", "description": "Shadow root." }
+                ],
+                "description": "Called when shadow root is pushed into the element.",
+                "hidden": true
+            },
+            {
+                "name": "shadowRootPopped",
+                "parameters": [
+                    { "name": "hostId", "$ref": "NodeId", "description": "Host element id." },
+                    { "name": "rootId", "$ref": "NodeId", "description": "Shadow root id." }
+                ],
+                "description": "Called when shadow root is popped from the element.",
+                "hidden": true
+            }
+        ]
+    },
+    {
+        "domain": "CSS",
+        "hidden": true,
+        "description": "This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) have an associated <code>id</code> used in subsequent operations on the related object. Each object type has a specific <code>id</code> structure, and those are not interchangeable between objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls (which accept a DOM node id). A client can also discover all the existing stylesheets with the <code>getAllStyleSheets()</code> method (or keeping track of the <code>styleSheetAdded</code>/<code>styleSheetRemoved</code> events) and subsequently load the required stylesheet contents using the <code>getStyleSheet[Text]()</code> methods.",
+        "types": [
+            {
+                "id": "StyleSheetId",
+                "type": "string"
+            },
+            {
+                "id": "CSSStyleId",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Enclosing stylesheet identifier." },
+                    { "name": "ordinal", "type": "integer", "description": "The style ordinal within the stylesheet." }
+                ],
+                "description": "This object identifies a CSS style in a unique way."
+            },
+            {
+                "id": "StyleSheetOrigin",
+                "type": "string",
+                "enum": ["user", "user-agent", "inspector", "regular"],
+                "description": "Stylesheet type: \"user\" for user stylesheets, \"user-agent\" for user-agent stylesheets, \"inspector\" for stylesheets created by the inspector (i.e. those holding the \"via inspector\" rules), \"regular\" for regular stylesheets."
+            },
+            {
+                "id": "CSSRuleId",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Enclosing stylesheet identifier." },
+                    { "name": "ordinal", "type": "integer", "description": "The rule ordinal within the stylesheet." }
+                ],
+                "description": "This object identifies a CSS rule in a unique way."
+            },
+            {
+                "id": "PseudoIdMatches",
+                "type": "object",
+                "properties": [
+                    { "name": "pseudoId", "type": "integer", "description": "Pseudo style identifier (see <code>enum PseudoId</code> in <code>RenderStyleConstants.h</code>)."},
+                    { "name": "matches", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules applicable to the pseudo style."}
+                ],
+                "description": "CSS rule collection for a single pseudo style."
+            },
+            {
+                "id": "InheritedStyleEntry",
+                "type": "object",
+                "properties": [
+                    { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "The ancestor node's inline style, if any, in the style inheritance chain." },
+                    { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules matching the ancestor node in the style inheritance chain." }
+                ],
+                "description": "CSS rule collection for a single pseudo style."
+            },
+            {
+                "id": "RuleMatch",
+                "type": "object",
+                "properties": [
+                    { "name": "rule", "$ref": "CSSRule", "description": "CSS rule in the match." },
+                    { "name": "matchingSelectors", "type": "array", "items": { "type": "integer" }, "description": "Matching selector indices in the rule's selectorList selectors (0-based)." }
+                ],
+                "description": "Match data for a CSS rule."
+            },
+            {
+                "id": "SelectorList",
+                "type": "object",
+                "properties": [
+                    { "name": "selectors", "type": "array", "items": { "type": "string" }, "description": "Selectors in the list." },
+                    { "name": "text", "type": "string", "description": "Rule selector text." },
+                    { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Rule selector range in the underlying resource (if available)." }
+                ],
+                "description": "Selector list data."
+            },
+            {
+                "id": "CSSStyleAttribute",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "DOM attribute name (e.g. \"width\")."},
+                    { "name": "style", "$ref": "CSSStyle", "description": "CSS style generated by the respective DOM attribute."}
+                ],
+                "description": "CSS style information for a DOM style attribute."
+            },
+            {
+                "id": "CSSStyleSheetHeader",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The stylesheet identifier."},
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Owner frame identifier."},
+                    { "name": "sourceURL", "type": "string", "description": "Stylesheet resource URL."},
+                    { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Stylesheet origin."},
+                    { "name": "title", "type": "string", "description": "Stylesheet title."},
+                    { "name": "disabled", "type": "boolean", "description": "Denotes whether the stylesheet is disabled."}
+                ],
+                "description": "CSS stylesheet metainformation."
+            },
+            {
+                "id": "CSSStyleSheetBody",
+                "type": "object",
+                "properties": [
+                    { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The stylesheet identifier."},
+                    { "name": "rules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "Stylesheet resource URL."},
+                    { "name": "text", "type": "string", "optional": true, "description": "Stylesheet resource contents (if available)."}
+                ],
+                "description": "CSS stylesheet contents."
+            },
+            {
+                "id": "CSSRule",
+                "type": "object",
+                "properties": [
+                    { "name": "ruleId", "$ref": "CSSRuleId", "optional": true, "description": "The CSS rule identifier (absent for user agent stylesheet and user-specified stylesheet rules)."},
+                    { "name": "selectorList", "$ref": "SelectorList", "description": "Rule selector data." },
+                    { "name": "sourceURL", "type": "string", "optional": true, "description": "Parent stylesheet resource URL (for regular rules)."},
+                    { "name": "sourceLine", "type": "integer", "description": "Line ordinal of the rule selector start character in the resource."},
+                    { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Parent stylesheet's origin."},
+                    { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." },
+                    { "name": "media", "type": "array", "items": { "$ref": "CSSMedia" }, "optional": true, "description": "Media list array (for rules involving media queries). The array enumerates media queries starting with the innermost one, going outwards." }
+                ],
+                "description": "CSS rule representation."
+            },
+            {
+                "id": "SourceRange",
+                "type": "object",
+                "properties": [
+                    { "name": "startLine", "type": "integer", "description": "Start line of range." },
+                    { "name": "startColumn", "type": "integer", "description": "Start column of range (inclusive)." },
+                    { "name": "endLine", "type": "integer", "description": "End line of range" },
+                    { "name": "endColumn", "type": "integer", "description": "End column of range (exclusive)." }
+                ],
+                "description": "Text range within a resource."
+            },
+            {
+                "id": "ShorthandEntry",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Shorthand name." },
+                    { "name": "value", "type": "string", "description": "Shorthand value." }
+                ]
+            },
+            {
+                "id": "CSSPropertyInfo",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Property name." },
+                    { "name": "longhands", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Longhand property names." }
+                ]
+            },
+            {
+                "id": "CSSComputedStyleProperty",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "Computed style property name." },
+                    { "name": "value", "type": "string", "description": "Computed style property value." }
+                ]
+            },
+            {
+                "id": "CSSStyle",
+                "type": "object",
+                "properties": [
+                    { "name": "styleId", "$ref": "CSSStyleId", "optional": true, "description": "The CSS style identifier (absent for attribute styles)." },
+                    { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSProperty" }, "description": "CSS properties in the style." },
+                    { "name": "shorthandEntries", "type": "array", "items": { "$ref": "ShorthandEntry" }, "description": "Computed values for all shorthands found in the style." },
+                    { "name": "cssText", "type": "string", "optional": true, "description": "Style declaration text (if available)." },
+                    { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Style declaration range in the enclosing stylesheet (if available)." },
+                    { "name": "width", "type": "string", "optional": true, "description": "The effective \"width\" property value from this style." },
+                    { "name": "height", "type": "string", "optional": true, "description": "The effective \"height\" property value from this style." }
+                ],
+                "description": "CSS style representation."
+            },
+            {
+                "id": "CSSProperty",
+                "type": "object",
+                "properties": [
+                    { "name": "name", "type": "string", "description": "The property name." },
+                    { "name": "value", "type": "string", "description": "The property value." },
+                    { "name": "priority", "type": "string", "optional": true, "description": "The property priority (implies \"\" if absent)." },
+                    { "name": "implicit", "type": "boolean", "optional": true, "description": "Whether the property is implicit (implies <code>false</code> if absent)." },
+                    { "name": "text", "type": "string", "optional": true, "description": "The full property text as specified in the style." },
+                    { "name": "parsedOk", "type": "boolean", "optional": true, "description": "Whether the property is understood by the browser (implies <code>true</code> if absent)." },
+                    { "name": "status", "type": "string", "enum": ["active", "inactive", "disabled", "style"], "optional": true, "description": "The property status: \"active\" if the property is effective in the style, \"inactive\" if the property is overridden by a same-named property in this style later on, \"disabled\" if the property is disabled by the user, \"style\" (implied if absent) if the property is reported by the browser rather than by the CSS source parser." },
+                    { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The entire property range in the enclosing style declaration (if available)." }
+                ],
+                "description": "CSS property declaration data."
+            },
+            {
+                "id": "CSSMedia",
+                "type": "object",
+                "properties": [
+                    { "name": "text", "type": "string", "description": "Media query text." },
+                    { "name": "source", "type": "string", "enum": ["mediaRule", "importRule", "linkedSheet", "inlineSheet"], "description": "Source of the media query: \"mediaRule\" if specified by a @media rule, \"importRule\" if specified by an @import rule, \"linkedSheet\" if specified by a \"media\" attribute in a linked stylesheet's LINK tag, \"inlineSheet\" if specified by a \"media\" attribute in an inline stylesheet's STYLE tag." },
+                    { "name": "sourceURL", "type": "string", "optional": true, "description": "URL of the document containing the media query description." },
+                    { "name": "sourceLine", "type": "integer", "optional": true, "description": "Line in the document containing the media query (not defined for the \"stylesheet\" source)." }
+                ],
+                "description": "CSS media query descriptor."
+            },
+            {
+                "id": "SelectorProfileEntry",
+                "type": "object",
+                "properties": [
+                    { "name": "selector", "type": "string", "description": "CSS selector of the corresponding rule." },
+                    { "name": "url", "type": "string", "description": "URL of the resource containing the corresponding rule." },
+                    { "name": "lineNumber", "type": "integer", "description": "Selector line number in the resource for the corresponding rule." },
+                    { "name": "time", "type": "number", "description": "Total time this rule handling contributed to the browser running time during profiling (in milliseconds.)" },
+                    { "name": "hitCount", "type": "integer", "description": "Number of times this rule was considered a candidate for matching against DOM elements." },
+                    { "name": "matchCount", "type": "integer", "description": "Number of times this rule actually matched a DOM element." }
+                ],
+                "description": "CSS selector profile entry."
+            },
+            {
+                "id": "SelectorProfile",
+                "type": "object",
+                "properties": [
+                    { "name": "totalTime", "type": "number", "description": "Total processing time for all selectors in the profile (in milliseconds.)" },
+                    { "name": "data", "type": "array", "items": { "$ref": "SelectorProfileEntry" }, "description": "CSS selector profile entries." }
+                ]
+            },
+            {
+                "id": "Region",
+                "type": "object",
+                "properties": [
+                    { "name": "regionOverset", "type": "string", "enum": ["overset", "fit", "empty"], "description": "The \"overset\" attribute of a Named Flow." },
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The corresponding DOM node id." }
+                ],
+                "description": "This object represents a region that flows from a Named Flow.",
+                "hidden": true
+            },
+            {
+                "id": "NamedFlow",
+                "type": "object",
+                "properties": [
+                    { "name": "documentNodeId", "$ref": "DOM.NodeId", "description": "The document node id." },
+                    { "name": "name", "type": "string", "description": "Named Flow identifier." },
+                    { "name": "overset", "type": "boolean", "description": "The \"overset\" attribute of a Named Flow." },
+                    { "name": "content", "type": "array", "items": { "$ref": "DOM.NodeId" }, "description": "An array of nodes that flow into the Named Flow." },
+                    { "name": "regions", "type": "array", "items": { "$ref": "Region" }, "description": "An array of regions associated with the Named Flow." }
+                ],
+                "description": "This object represents a Named Flow.",
+                "hidden": true
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables the CSS agent for the given page. Clients should not assume that the CSS agent has been enabled until the result of this command is received."
+            },
+            {
+                "name": "disable",
+                "description": "Disables the CSS agent for the given page."
+            },
+            {
+                "name": "getMatchedStylesForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId" },
+                    { "name": "includePseudo", "type": "boolean", "optional": true, "description": "Whether to include pseudo styles (default: true)." },
+                    { "name": "includeInherited", "type": "boolean", "optional": true, "description": "Whether to include inherited styles (default: true)." }
+                ],
+                "returns": [
+                    { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "optional": true, "description": "CSS rules matching this node, from all applicable stylesheets." },
+                    { "name": "pseudoElements", "type": "array", "items": { "$ref": "PseudoIdMatches" }, "optional": true, "description": "Pseudo style matches for this node." },
+                    { "name": "inherited", "type": "array", "items": { "$ref": "InheritedStyleEntry" }, "optional": true, "description": "A chain of inherited styles (from the immediate node parent up to the DOM tree root)." }
+                ],
+                "description": "Returns requested styles for a DOM node identified by <code>nodeId</code>."
+            },
+            {
+                "name": "getInlineStylesForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId" }
+                ],
+                "returns": [
+                    { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "Inline style for the specified DOM node." },
+                    { "name": "attributesStyle", "$ref": "CSSStyle", "optional": true, "description": "Attribute-defined element style (e.g. resulting from \"width=20 height=100%\")."}
+                ],
+                "description": "Returns the styles defined inline (explicitly in the \"style\" attribute and implicitly, using DOM attributes) for a DOM node identified by <code>nodeId</code>."
+            },
+            {
+                "name": "getComputedStyleForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId" }
+                ],
+                "returns": [
+                    { "name": "computedStyle", "type": "array", "items": { "$ref": "CSSComputedStyleProperty" }, "description": "Computed style for the specified DOM node." }
+                ],
+                "description": "Returns the computed style for a DOM node identified by <code>nodeId</code>."
+            },
+            {
+                "name": "getAllStyleSheets",
+                "returns": [
+                    { "name": "headers", "type": "array", "items": { "$ref": "CSSStyleSheetHeader" }, "description": "Descriptor entries for all available stylesheets." }
+                ],
+                "description": "Returns metainfo entries for all known stylesheets."
+            },
+            {
+                "name": "getStyleSheet",
+                "parameters": [
+                    { "name": "styleSheetId", "$ref": "StyleSheetId" }
+                ],
+                "returns": [
+                    { "name": "styleSheet", "$ref": "CSSStyleSheetBody", "description": "Stylesheet contents for the specified <code>styleSheetId</code>." }
+                ],
+                "description": "Returns stylesheet data for the specified <code>styleSheetId</code>."
+            },
+            {
+                "name": "getStyleSheetText",
+                "parameters": [
+                    { "name": "styleSheetId", "$ref": "StyleSheetId" }
+                ],
+                "returns": [
+                    { "name": "text", "type": "string", "description": "The stylesheet text." }
+                ],
+                "description": "Returns the current textual content and the URL for a stylesheet."
+            },
+            {
+                "name": "setStyleSheetText",
+                "parameters": [
+                    { "name": "styleSheetId", "$ref": "StyleSheetId" },
+                    { "name": "text", "type": "string" }
+                ],
+                "description": "Sets the new stylesheet text, thereby invalidating all existing <code>CSSStyleId</code>'s and <code>CSSRuleId</code>'s contained by this stylesheet."
+            },
+            {
+                "name": "setStyleText",
+                "parameters": [
+                    { "name": "styleId", "$ref": "CSSStyleId" },
+                    { "name": "text", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the text modification." }
+                ],
+                "description": "Updates the CSSStyleDeclaration text."
+            },
+            {
+                "name": "setPropertyText",
+                "parameters": [
+                    { "name": "styleId", "$ref": "CSSStyleId" },
+                    { "name": "propertyIndex", "type": "integer" },
+                    { "name": "text", "type": "string" },
+                    { "name": "overwrite", "type": "boolean" }
+                ],
+                "returns": [
+                    { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property text modification." }
+                ],
+                "description": "Sets the new <code>text</code> for a property in the respective style, at offset <code>propertyIndex</code>. If <code>overwrite</code> is <code>true</code>, a property at the given offset is overwritten, otherwise inserted. <code>text</code> entirely replaces the property <code>name: value</code>."
+            },
+            {
+                "name": "toggleProperty",
+                "parameters": [
+                    { "name": "styleId", "$ref": "CSSStyleId" },
+                    { "name": "propertyIndex", "type": "integer" },
+                    { "name": "disable", "type": "boolean" }
+                ],
+                "returns": [
+                    { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property toggling." }
+                ],
+                "description": "Toggles the property in the respective style, at offset <code>propertyIndex</code>. The <code>disable</code> parameter denotes whether the property should be disabled (i.e. removed from the style declaration). If <code>disable == false</code>, the property gets put back into its original place in the style declaration."
+            },
+            {
+                "name": "setRuleSelector",
+                "parameters": [
+                    { "name": "ruleId", "$ref": "CSSRuleId" },
+                    { "name": "selector", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "rule", "$ref": "CSSRule", "description": "The resulting rule after the selector modification." }
+                ],
+                "description": "Modifies the rule selector."
+            },
+            {
+                "name": "addRule",
+                "parameters": [
+                    { "name": "contextNodeId", "$ref": "DOM.NodeId" },
+                    { "name": "selector", "type": "string" }
+                ],
+                "returns": [
+                    { "name": "rule", "$ref": "CSSRule", "description": "The newly created rule." }
+                ],
+                "description": "Creates a new empty rule with the given <code>selector</code> in a special \"inspector\" stylesheet in the owner document of the context node."
+            },
+            {
+                "name": "getSupportedCSSProperties",
+                "returns": [
+                    { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSPropertyInfo" }, "description": "Supported property metainfo." }
+                ],
+                "description": "Returns all supported CSS property names."
+            },
+            {
+                "name": "forcePseudoState",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The element id for which to force the pseudo state." },
+                    { "name": "forcedPseudoClasses", "type": "array", "items": { "type": "string", "enum": ["active", "focus", "hover", "visited"] }, "description": "Element pseudo classes to force when computing the element's style." }
+                ],
+                "description": "Ensures that the given node will have specified pseudo-classes whenever its style is computed by the browser."
+            },
+            {
+                "name": "startSelectorProfiler"
+            },
+            {
+                "name": "stopSelectorProfiler",
+                "returns": [
+                    { "name": "profile", "$ref": "SelectorProfile" }
+                ]
+            },
+            {
+                "name": "getNamedFlowCollection",
+                "parameters": [
+                    { "name": "documentNodeId", "$ref": "DOM.NodeId", "description": "The document node id for which to get the Named Flow Collection." }
+                ],
+                "returns": [
+                    { "name": "namedFlows", "type": "array", "items": { "$ref": "NamedFlow" }, "description": "An array containing the Named Flows in the document." }
+                ],
+                "description": "Returns the Named Flows from the document.",
+                "hidden": true
+            }
+        ],
+        "events": [
+            {
+                "name": "mediaQueryResultChanged",
+                "description": "Fires whenever a MediaQuery result changes (for example, after a browser window has been resized.) The current implementation considers only viewport-dependent media features."
+            },
+            {
+                "name": "styleSheetChanged",
+                "parameters": [
+                    { "name": "styleSheetId", "$ref": "StyleSheetId" }
+                ],
+                "description": "Fired whenever a stylesheet is changed as a result of the client operation."
+            },
+            {
+                "name": "styleSheetAdded",
+                "parameters": [
+                    { "name": "header", "$ref": "CSSStyleSheetHeader", "description": "Added stylesheet metainfo." }
+                ],
+                "description": "Fired whenever an active document stylesheet is added."
+            },
+            {
+                "name": "styleSheetRemoved",
+                "parameters": [
+                    { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the removed stylesheet." }
+                ],
+                "description": "Fired whenever an active document stylesheet is removed."
+            },
+            {
+                "name": "namedFlowCreated",
+                "parameters": [
+                    { "name": "namedFlow", "$ref": "NamedFlow", "description": "The new Named Flow." }
+                ],
+                "description": "Fires when a Named Flow is created.",
+                "hidden": true
+            },
+            {
+                "name": "namedFlowRemoved",
+                "parameters": [
+                    { "name": "documentNodeId", "$ref": "DOM.NodeId", "description": "The document node id." },
+                    { "name": "flowName", "type": "string", "description": "Identifier of the removed Named Flow." }
+                ],
+                "description": "Fires when a Named Flow is removed: has no associated content nodes and regions.",
+                "hidden": true
+            },
+            {
+                "name": "regionLayoutUpdated",
+                "parameters": [
+                    { "name": "namedFlow", "$ref": "NamedFlow", "description": "The Named Flow whose layout may have changed." }
+                ],
+                "description": "Fires when a Named Flow's layout may have changed.",
+                "hidden": true
+            }
+        ]
+    },
+    {
+        "domain": "Timeline",
+        "description": "Timeline provides its clients with instrumentation records that are generated during the page runtime. Timeline instrumentation can be started and stopped using corresponding commands. While timeline is started, it is generating timeline event records.",
+        "types": [
+            {
+                "id": "DOMCounters",
+                "type": "object",
+                "properties": [
+                    { "name": "documents", "type": "integer" },
+                    { "name": "nodes", "type": "integer" },
+                    { "name": "jsEventListeners", "type": "integer" }
+                ],
+                "description": "Current values of DOM counters.",
+                "hidden": true
+            },
+            {
+                "id": "TimelineEvent",
+                "type": "object",
+                "properties": [
+                    { "name": "type", "type": "string", "description": "Event type." },
+                    { "name": "thread", "type": "string", "optional": true, "description": "If present, identifies the thread that produced the event.", "hidden": true },
+                    { "name": "data", "type": "object", "description": "Event data." },
+                    { "name": "children", "type": "array", "optional": true, "items": { "$ref": "TimelineEvent" }, "description": "Nested records." },
+                    { "name": "counters", "$ref": "DOMCounters", "optional": true, "hidden": true, "description": "Current values of DOM counters." },
+                    { "name": "usedHeapSize", "type": "integer", "optional": true, "hidden": true, "description": "Current size of JS heap." },
+                    { "name": "nativeHeapStatistics", "type": "object", "optional": true, "hidden": true, "description": "Native heap statistics." }
+                ],
+                "description": "Timeline record contains information about the recorded activity."
+            }
+        ],
+        "commands": [
+            {
+                "name": "start",
+                "parameters": [
+                    { "name": "maxCallStackDepth", "optional": true, "type": "integer", "description": "Samples JavaScript stack traces up to <code>maxCallStackDepth</code>, defaults to 5." },
+                    { "name": "includeDomCounters", "optional": true, "type": "boolean", "hidden": true, "description": "Whether DOM counters data should be included into timeline events." },
+                    { "name": "includeNativeMemoryStatistics", "optional": true, "type": "boolean", "hidden": true, "description": "Whether native memory usage statistics should be reported as part of timeline events." }
+
+                ],
+                "description": "Starts capturing instrumentation events."
+            },
+            {
+                "name": "stop",
+                "description": "Stops capturing instrumentation events."
+            }
+        ],
+        "events": [
+            {
+                "name": "eventRecorded",
+                "parameters": [
+                    { "name": "record", "$ref": "TimelineEvent", "description": "Timeline event record data." }
+                ],
+                "description": "Fired for every instrumentation event while timeline is started."
+            }
+        ]
+    },
+    {
+        "domain": "Debugger",
+        "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.",
+        "types": [
+            {
+                "id": "BreakpointId",
+                "type": "string",
+                "description": "Breakpoint identifier."
+            },
+            {
+                "id": "ScriptId",
+                "type": "string",
+                "description": "Unique script identifier."
+            },
+            {
+                "id": "CallFrameId",
+                "type": "string",
+                "description": "Call frame identifier."
+            },
+            {
+                "id": "Location",
+                "type": "object",
+                "properties": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." },
+                    { "name": "lineNumber", "type": "integer", "description": "Line number in the script." },
+                    { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script." }
+                ],
+                "description": "Location in the source code."
+            },
+            {
+                "id": "FunctionDetails",
+                "hidden": true,
+                "type": "object",
+                "properties": [
+                    { "name": "location", "$ref": "Location", "description": "Location of the function." },
+                    { "name": "name", "type": "string", "optional": true, "description": "Name of the function. Not present for anonymous functions." },
+                    { "name": "displayName", "type": "string", "optional": true, "description": "Display name of the function(specified in 'displayName' property on the function object)." },
+                    { "name": "inferredName", "type": "string", "optional": true, "description": "Name of the function inferred from its initial assignment." },
+                    { "name": "scopeChain", "type": "array", "optional": true, "items": { "$ref": "Scope" }, "description": "Scope chain for this closure." }
+                ],
+                "description": "Information about the function."
+            },
+            {
+                "id": "CallFrame",
+                "type": "object",
+                "properties": [
+                    { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." },
+                    { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." },
+                    { "name": "location", "$ref": "Location", "description": "Location in the source code." },
+                    { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." },
+                    { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." }
+                ],
+                "description": "JavaScript call frame. Array of call frames form the call stack."
+            },
+            {
+                "id": "Scope",
+                "type": "object",
+                "properties": [
+                    { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch"], "description": "Scope type." },
+                    { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }
+                ],
+                "description": "Scope description."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received."
+            },
+            {
+                "name": "disable",
+                "description": "Disables debugger for given page."
+            },
+            {
+                "name": "setBreakpointsActive",
+                "parameters": [
+                    { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." }
+                ],
+                "description": "Activates / deactivates all breakpoints on the page."
+            },
+            {
+                "name": "setBreakpointByUrl",
+                "parameters": [
+                    { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." },
+                    { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." },
+                    { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." },
+                    { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." },
+                    { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
+                ],
+                "returns": [
+                    { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
+                    { "name": "locations", "type": "array", "items": { "$ref": "Location"}, "description": "List of the locations this breakpoint resolved into upon addition." }
+                ],
+                "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads."
+            },
+            {
+                "name": "setBreakpoint",
+                "parameters": [
+                    { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." },
+                    { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
+                ],
+                "returns": [
+                    { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
+                    { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." }
+                ],
+                "description": "Sets JavaScript breakpoint at a given location."
+            },
+            {
+                "name": "removeBreakpoint",
+                "parameters": [
+                    { "name": "breakpointId", "$ref": "BreakpointId" }
+                ],
+                "description": "Removes JavaScript breakpoint."
+            },
+            {
+                "name": "continueToLocation",
+                "parameters": [
+                    { "name": "location", "$ref": "Location", "description": "Location to continue to." }
+                ],
+                "description": "Continues execution until specific location is reached."
+            },
+            {
+                "name": "stepOver",
+                "description": "Steps over the statement."
+            },
+            {
+                "name": "stepInto",
+                "description": "Steps into the function call."
+            },
+            {
+                "name": "stepOut",
+                "description": "Steps out of the function call."
+            },
+            {
+                "name": "pause",
+                "description": "Stops on the next JavaScript statement."
+            },
+            {
+                "name": "resume",
+                "description": "Resumes JavaScript execution."
+            },
+            {
+                "name": "searchInContent",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to search in." },
+                    { "name": "query", "type": "string", "description": "String to search for."  },
+                    { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." },
+                    { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." }
+                ],
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "Page.SearchMatch" }, "description": "List of search matches." }
+                ],
+                "description": "Searches for given string in script content."
+            },
+            {
+                "name": "canSetScriptSource",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "True if <code>setScriptSource</code> is supported." }
+                ],
+                "description": "Always returns true."
+            },
+            {
+                "name": "setScriptSource",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to edit." },
+                    { "name": "scriptSource", "type": "string", "description": "New content of the script." },
+                    { "name": "preview", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", "hidden": true }
+                ],
+                "returns": [
+                    { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame"}, "description": "New stack trace in case editing has happened while VM was stopped." },
+                    { "name": "result", "type": "object", "optional": true, "description": "VM-specific description of the changes applied.", "hidden": true }
+                ],
+                "description": "Edits JavaScript source live."
+            },
+            {
+                "name": "restartFrame",
+                "parameters": [
+                    { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }
+                ],
+                "returns": [
+                    { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame"}, "description": "New stack trace." },
+                    { "name": "result", "type": "object", "description": "VM-specific description.", "hidden": true }
+                ],
+                "hidden": true,
+                "description": "Restarts particular call frame from the beginning."
+            },
+            {
+                "name": "getScriptSource",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to get source for." }
+                ],
+                "returns": [
+                    { "name": "scriptSource", "type": "string", "description": "Script source." }
+                ],
+                "description": "Returns source for the script with given id."
+            },
+            {
+                "name": "getFunctionDetails",
+                "hidden": true,
+                "parameters": [
+                    { "name": "functionId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the function to get location for." }
+                ],
+                "returns": [
+                    { "name": "details", "$ref": "FunctionDetails", "description": "Information about the function." }
+                ],
+                "description": "Returns detailed informtation on given function."
+            },
+            {
+                "name": "setPauseOnExceptions",
+                "parameters": [
+                    { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." }
+                ],
+                "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>."
+            },
+            {
+                "name": "evaluateOnCallFrame",
+                "parameters": [
+                    { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." },
+                    { "name": "expression", "type": "string", "description": "Expression to evaluate." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." },
+                    { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true },
+                    { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true },
+                    { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." },
+                    { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
+                ],
+                "description": "Evaluates expression on a given call frame."
+            },
+            {
+                "name": "compileScript",
+                "hidden": true,
+                "parameters": [
+                    { "name": "expression", "type": "string", "description": "Expression to compile." },
+                    { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." }
+                ],
+                "returns": [
+                    { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." },
+                    { "name": "syntaxErrorMessage", "type": "string", "optional": true, "description": "Syntax error message if compilation failed." }
+                ],
+                "description": "Compiles expression."
+            },
+            {
+                "name": "runScript",
+                "hidden": true,
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." },
+                    { "name": "contextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." },
+                    { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Run result." },
+                    { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the script run." }
+                ],
+                "description": "Runs script with given id in a given context."
+            },
+            {
+                "name": "setOverlayMessage",
+                "parameters": [
+                    { "name": "message", "type": "string", "optional": true, "description": "Overlay message to display when paused in debugger." }
+                ],
+                "hidden": true,
+                "description": "Sets overlay message."
+            },
+            {
+                "name": "setVariableValue",
+                "parameters": [
+                    { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." },
+                    { "name": "variableName", "type": "string", "description": "Variable name." },
+                    { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." },
+                    { "name": "callFrameId", "$ref": "CallFrameId", "optional": true, "description": "Id of callframe that holds variable." },
+                    { "name": "functionObjectId", "$ref": "Runtime.RemoteObjectId", "optional": true, "description": "Object id of closure (function) that holds variable." }
+                ],
+                "hidden": true,
+                "description": "Changes value of variable in a callframe or a closure. Either callframe or function must be specified. Object-based scopes are not supported and must be mutated manually."
+            }
+        ],
+        "events": [
+            {
+                "name": "globalObjectCleared",
+                "description": "Called when global has been cleared and debugger client should reset its state. Happens upon navigation or reload."
+            },
+            {
+                "name": "scriptParsed",
+                "parameters": [
+                    { "name": "scriptId", "$ref": "ScriptId", "description": "Identifier of the script parsed." },
+                    { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." },
+                    { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." },
+                    { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." },
+                    { "name": "endLine", "type": "integer", "description": "Last line of the script." },
+                    { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." },
+                    { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." },
+                    { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." },
+                    { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true }
+                ],
+                "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger."
+            },
+            {
+                "name": "scriptFailedToParse",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL of the script that failed to parse." },
+                    { "name": "scriptSource", "type": "string", "description": "Source text of the script that failed to parse." },
+                    { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource." },
+                    { "name": "errorLine", "type": "integer", "description": "Line with error." },
+                    { "name": "errorMessage", "type": "string", "description": "Parse error message." }
+                ],
+                "description": "Fired when virtual machine fails to parse the script."
+            },
+            {
+                "name": "breakpointResolved",
+                "parameters": [
+                    { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." },
+                    { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." }
+                ],
+                "description": "Fired when breakpoint is resolved to an actual script and location."
+            },
+            {
+                "name": "paused",
+                "parameters": [
+                    { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." },
+                    { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "CSPViolation", "other" ], "description": "Pause reason." },
+                    { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }
+                ],
+                "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria."
+            },
+            {
+                "name": "resumed",
+                "description": "Fired when the virtual machine resumed execution."
+            }
+        ]
+    },
+    {
+        "domain": "DOMDebugger",
+        "description": "DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript execution will stop on these operations as if there was a regular breakpoint set.",
+        "types": [
+            {
+                "id": "DOMBreakpointType",
+                "type": "string",
+                "enum": ["subtree-modified", "attribute-modified", "node-removed"],
+                "description": "DOM breakpoint type."
+            }
+        ],
+        "commands": [
+            {
+                "name": "setDOMBreakpoint",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to set breakpoint on." },
+                    { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the operation to stop upon." }
+                ],
+                "description": "Sets breakpoint on particular operation with DOM."
+            },
+            {
+                "name": "removeDOMBreakpoint",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to remove breakpoint from." },
+                    { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the breakpoint to remove." }
+                ],
+                "description": "Removes DOM breakpoint that was set using <code>setDOMBreakpoint</code>."
+            },
+            {
+                "name": "setEventListenerBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "DOM Event name to stop on (any DOM event will do)." }
+                ],
+                "description": "Sets breakpoint on particular DOM event."
+            },
+            {
+                "name": "removeEventListenerBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "Event name." }
+                ],
+                "description": "Removes breakpoint on particular DOM event."
+            },
+            {
+                "name": "setInstrumentationBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." }
+                ],
+                "description": "Sets breakpoint on particular native event.",
+                "hidden": true
+            },
+            {
+                "name": "removeInstrumentationBreakpoint",
+                "parameters": [
+                    { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." }
+                ],
+                "description": "Sets breakpoint on particular native event.",
+                "hidden": true
+            },
+            {
+                "name": "setXHRBreakpoint",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "Resource URL substring. All XHRs having this substring in the URL will get stopped upon." }
+                ],
+                "description": "Sets breakpoint on XMLHttpRequest."
+            },
+            {
+                "name": "removeXHRBreakpoint",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "Resource URL substring." }
+                ],
+                "description": "Removes breakpoint from XMLHttpRequest."
+            }
+        ]
+    },
+    {
+        "domain": "Profiler",
+        "hidden": true,
+        "types": [
+            {
+                "id": "ProfileHeader",
+                "type": "object",
+                "description": "Profile header.",
+                "properties": [
+                    { "name": "typeId", "type": "string", "enum": ["CPU", "CSS", "HEAP"], "description": "Profile type name." },
+                    { "name": "title", "type": "string", "description": "Profile title." },
+                    { "name": "uid", "type": "integer", "description": "Unique identifier of the profile." }
+                ]
+            },
+            {
+                "id": "CPUProfileNode",
+                "type": "object",
+                "description": "CPU Profile node. Holds callsite information, execution statistics and child nodes.",
+                "properties": [
+                    { "name": "functionName", "type": "string", "description": "Function name." },
+                    { "name": "url", "type": "string", "description": "URL." },
+                    { "name": "lineNumber", "type": "integer", "description": "Line number." },
+                    { "name": "totalTime", "type": "number", "description": "Total execution time." },
+                    { "name": "selfTime", "type": "number", "description": "Self time." },
+                    { "name": "numberOfCalls", "type": "integer", "description": "Number of calls." },
+                    { "name": "visible", "type": "boolean", "description": "Visibility." },
+                    { "name": "callUID", "type": "number", "description": "Call UID." },
+                    { "name": "children", "type": "array", "items": { "$ref": "CPUProfileNode" }, "description": "Child nodes." },
+                    { "name": "id", "optional": true, "type": "integer", "description": "Unique id of the node." }
+                ]
+            },
+            {
+                "id": "CPUProfile",
+                "type": "object",
+                "description": "Profile.",
+                "properties": [
+                    { "name": "head", "$ref": "CPUProfileNode", "optional": true },
+                    { "name": "idleTime", "type": "number", "optional": true },
+                    { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." }
+                ]
+            },
+            {
+                "id": "HeapSnapshotObjectId",
+                "type": "string",
+                "description": "Heap snashot object id."
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable"
+            },
+            {
+                "name": "disable"
+            },
+            {
+                "name": "start"
+            },
+            {
+                "name": "stop",
+                "returns": [
+                    { "name": "header", "$ref": "ProfileHeader", "description": "The header of the recorded profile."}
+                ]
+            },
+            {
+                "name": "getProfileHeaders",
+                "returns": [
+                    { "name": "headers", "type": "array", "items": { "$ref": "ProfileHeader"} }
+                ]
+            },
+            {
+                "name": "getCPUProfile",
+                "parameters": [
+                    { "name": "uid", "type": "integer" }
+                ],
+                "returns": [
+                    { "name": "profile", "$ref": "CPUProfile" }
+                ]
+            },
+            {
+                "name": "removeProfile",
+                "parameters": [
+                    { "name": "type", "type": "string" },
+                    { "name": "uid", "type": "integer" }
+                ]
+            },
+            {
+                "name": "clearProfiles"
+            }
+        ],
+        "events": [
+            {
+                "name": "addProfileHeader",
+                "parameters": [
+                    { "name": "header", "$ref": "ProfileHeader" }
+                ]
+            },
+            {
+                "name": "setRecordingProfile",
+                "parameters": [
+                    { "name": "isProfiling", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "resetProfiles"
+            }
+        ]
+    },
+    {
+        "domain": "HeapProfiler",
+        "hidden": true,
+        "types": [
+            {
+                "id": "ProfileHeader",
+                "type": "object",
+                "description": "Profile header.",
+                "properties": [
+                    { "name": "title", "type": "string", "description": "Profile title." },
+                    { "name": "uid", "type": "integer", "description": "Unique identifier of the profile." },
+                    { "name": "maxJSObjectId", "type": "integer", "optional": true, "description": "Last seen JS object Id." }
+                ]
+            },
+            {
+                "id": "HeapSnapshotObjectId",
+                "type": "string",
+                "description": "Heap snashot object id."
+            }
+        ],
+        "commands": [
+            {
+                "name": "getProfileHeaders",
+                "returns": [
+                    { "name": "headers", "type": "array", "items": { "$ref": "ProfileHeader"} }
+                ]
+            },
+            {
+                "name": "startTrackingHeapObjects"
+            },
+            {
+                "name": "stopTrackingHeapObjects"
+            },
+            {
+                "name": "getHeapSnapshot",
+                "parameters": [
+                    { "name": "uid", "type": "integer" }
+                ]
+            },
+            {
+                "name": "removeProfile",
+                "parameters": [
+                    { "name": "uid", "type": "integer" }
+                ]
+            },
+            {
+                "name": "clearProfiles"
+            },
+            {
+                "name": "takeHeapSnapshot",
+                "parameters": [
+                    { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." }
+                ]
+            },
+            {
+                "name": "collectGarbage"
+            },
+            {
+                "name": "getObjectByHeapObjectId",
+                "parameters": [
+                    { "name": "objectId", "$ref": "HeapSnapshotObjectId" },
+                    { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }
+                ],
+                "returns": [
+                    { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." }
+                ]
+            },
+            {
+                "name": "getHeapObjectId",
+                "parameters": [
+                    { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." }
+                ],
+                "returns": [
+                    { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "addProfileHeader",
+                "parameters": [
+                    { "name": "header", "$ref": "ProfileHeader" }
+                ]
+            },
+            {
+                "name": "addHeapSnapshotChunk",
+                "parameters": [
+                    { "name": "uid", "type": "integer" },
+                    { "name": "chunk", "type": "string" }
+                ]
+            },
+            {
+                "name": "finishHeapSnapshot",
+                "parameters": [
+                    { "name": "uid", "type": "integer" }
+                ]
+            },
+            {
+                "name": "resetProfiles"
+            },
+            {
+                "name": "reportHeapSnapshotProgress",
+                "parameters": [
+                    { "name": "done", "type": "integer" },
+                    { "name": "total", "type": "integer" }
+                ]
+            },
+            {
+                "name": "lastSeenObjectId",
+                "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.",
+                "parameters": [
+                    { "name": "lastSeenObjectId", "type": "integer" },
+                    { "name": "timestamp", "type": "number" }
+                ]
+            },
+            {
+                "name": "heapStatsUpdate",
+                "description": "If heap objects tracking has been started then backend may send update for one or more fragments",
+                "parameters": [
+                    { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."}
+                ]
+            }
+        ]
+    },
+    {
+        "domain": "Worker",
+        "hidden": true,
+        "types": [],
+        "commands": [
+            {
+                "name": "enable"
+            },
+            {
+                "name": "disable"
+            },
+            {
+                "name": "sendMessageToWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" },
+                    { "name": "message", "type": "object" }
+                ]
+            },
+            {
+                "name": "canInspectWorkers",
+                "description": "Tells whether browser supports workers inspection.",
+                "returns": [
+                    { "name": "result", "type": "boolean", "description": "True if browser has workers support." }
+                ]
+            },
+            {
+                "name": "connectToWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" }
+                ]
+            },
+            {
+                "name": "disconnectFromWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" }
+                ]
+            },
+            {
+                "name": "setAutoconnectToWorkers",
+                "parameters": [
+                    { "name": "value", "type": "boolean" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "workerCreated",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" },
+                    { "name": "url", "type": "string" },
+                    { "name": "inspectorConnected", "type": "boolean" }
+                ]
+            },
+            {
+                "name": "workerTerminated",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" }
+                ]
+            },
+            {
+                "name": "dispatchMessageFromWorker",
+                "parameters": [
+                    { "name": "workerId", "type": "integer" },
+                    { "name": "message", "type": "object" }
+                ]
+            },
+            {
+                "name": "disconnectedFromWorker"
+            }
+        ]
+    },
+    {
+        "domain": "Canvas",
+        "hidden": true,
+        "types": [
+            {
+                "id": "ResourceId",
+                "type": "string",
+                "description": "Unique resource identifier."
+            },
+            {
+                "id": "ResourceInfo",
+                "type": "object",
+                "properties": [
+                    { "name": "id", "$ref": "ResourceId" },
+                    { "name": "description", "type": "string" }
+                ]
+            },
+            {
+                "id": "ResourceState",
+                "type": "object",
+                "properties": [
+                    { "name": "id", "$ref": "ResourceId" },
+                    { "name": "traceLogId", "$ref": "TraceLogId" },
+                    { "name": "imageURL", "type": "string", "optional": true, "description": "Screenshot image data URL." }
+                ]
+            },
+            {
+                "id": "CallArgument",
+                "type": "object",
+                "properties": [
+                    { "name": "description", "type": "string" }
+                ]
+            },
+            {
+                "id": "Call",
+                "type": "object",
+                "properties": [
+                    { "name": "contextId", "$ref": "ResourceId" },
+                    { "name": "functionName", "type": "string", "optional": true },
+                    { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument" }, "optional": true },
+                    { "name": "result", "$ref": "CallArgument", "optional": true },
+                    { "name": "isDrawingCall", "type": "boolean", "optional": true },
+                    { "name": "isFrameEndCall", "type": "boolean", "optional": true },
+                    { "name": "property", "type": "string", "optional": true },
+                    { "name": "value", "$ref": "CallArgument", "optional": true },
+                    { "name": "sourceURL", "type": "string", "optional": true },
+                    { "name": "lineNumber", "type": "integer", "optional": true },
+                    { "name": "columnNumber", "type": "integer", "optional": true }
+                ]
+            },
+            {
+                "id": "TraceLogId",
+                "type": "string",
+                "description": "Unique trace log identifier."
+            },
+            {
+                "id": "TraceLog",
+                "type": "object",
+                "properties": [
+                    { "name": "id", "$ref": "TraceLogId" },
+                    { "name": "calls", "type": "array", "items": { "$ref": "Call" } },
+                    { "name": "startOffset", "type": "integer" },
+                    { "name": "alive", "type": "boolean" },
+                    { "name": "totalAvailableCalls", "type": "number" }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables Canvas inspection."
+            },
+            {
+                "name": "disable",
+                "description": "Disables Canvas inspection."
+            },
+            {
+                "name": "dropTraceLog",
+                "parameters": [
+                    { "name": "traceLogId", "$ref": "TraceLogId" }
+                ]
+            },
+            {
+                "name": "hasUninstrumentedCanvases",
+                "returns": [
+                    { "name": "result", "type": "boolean" }
+                ],
+                "description": "Checks if there is any uninstrumented canvas in the inspected page."
+            },
+            {
+                "name": "captureFrame",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "optional": true, "description": "Identifier of the frame containing document whose canvases are to be captured. If omitted, main frame is assumed." }
+                ],
+                "returns": [
+                    { "name": "traceLogId", "$ref": "TraceLogId", "description": "Identifier of the trace log containing captured canvas calls." }
+                ],
+                "description": "Starts (or continues) a canvas frame capturing which will be stopped automatically after the next frame is prepared."
+            },
+            {
+                "name": "startCapturing",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "optional": true, "description": "Identifier of the frame containing document whose canvases are to be captured. If omitted, main frame is assumed." }
+                ],
+                "returns": [
+                    { "name": "traceLogId", "$ref": "TraceLogId", "description": "Identifier of the trace log containing captured canvas calls." }
+                ],
+                "description": "Starts (or continues) consecutive canvas frames capturing. The capturing is stopped by the corresponding stopCapturing command."
+            },
+            {
+                "name": "stopCapturing",
+                "parameters": [
+                    { "name": "traceLogId", "$ref": "TraceLogId" }
+                ]
+            },
+            {
+                "name": "getTraceLog",
+                "parameters": [
+                    { "name": "traceLogId", "$ref": "TraceLogId" },
+                    { "name": "startOffset", "type": "integer", "optional": true },
+                    { "name": "maxLength", "type": "integer", "optional": true }
+                ],
+                "returns": [
+                    { "name": "traceLog", "$ref": "TraceLog" }
+                ]
+            },
+            {
+                "name": "replayTraceLog",
+                "parameters": [
+                    { "name": "traceLogId", "$ref": "TraceLogId" },
+                    { "name": "stepNo", "type": "integer" }
+                ],
+                "returns": [
+                    { "name": "resourceState", "$ref": "ResourceState" }
+                ]
+            },
+            {
+                "name": "getResourceInfo",
+                "parameters": [
+                    { "name": "resourceId", "$ref": "ResourceId" }
+                ],
+                "returns": [
+                    { "name": "resourceInfo", "$ref": "ResourceInfo" }
+                ]
+            },
+            {
+                "name": "getResourceState",
+                "parameters": [
+                    { "name": "traceLogId", "$ref": "TraceLogId" },
+                    { "name": "resourceId", "$ref": "ResourceId" }
+                ],
+                "returns": [
+                    { "name": "resourceState", "$ref": "ResourceState" }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "contextCreated",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame containing a canvas with a context." }
+                ],
+                "description": "Fired when a canvas context has been created in the given frame. The context may not be instrumented (see hasUninstrumentedCanvases command)."
+            },
+            {
+                "name": "traceLogsRemoved",
+                "parameters": [
+                    { "name": "frameId", "$ref": "Network.FrameId", "optional": true, "description": "If given, trace logs from the given frame were removed." },
+                    { "name": "traceLogId", "$ref": "TraceLogId", "optional": true, "description": "If given, trace log with the given ID was removed." }
+                ],
+                "description": "Fired when a set of trace logs were removed from the backend. If no parameters are given, all trace logs were removed."
+            }
+        ]
+    },
+    {
+        "domain": "Input",
+        "types": [],
+        "commands": [
+            {
+                "name": "dispatchKeyEvent",
+                "parameters": [
+                    { "name": "type", "type": "string", "enum": ["keyDown", "keyUp", "rawKeyDown", "char"], "description": "Type of the key event." },
+                    { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." },
+                    { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." },
+                    { "name": "text", "type": "string", "optional": true, "description": "Text as generated by processing a virtual key code with a keyboard layout. Not needed for for <code>keyUp</code> and <code>rawKeyDown</code> events (default: \"\")" },
+                    { "name": "unmodifiedText", "type": "string", "optional": true, "description": "Text that would have been generated by the keyboard if no modifiers were pressed (except for shift). Useful for shortcut (accelerator) key handling (default: \"\")." },
+                    { "name": "keyIdentifier", "type": "string", "optional": true, "description": "Unique key identifier (e.g., 'U+0041') (default: \"\")." },
+                    { "name": "windowsVirtualKeyCode", "type": "integer", "optional": true, "description": "Windows virtual key code (default: 0)." },
+                    { "name": "nativeVirtualKeyCode", "type": "integer", "optional": true, "description": "Native virtual key code (default: 0)." },
+                    { "name": "macCharCode", "type": "integer", "optional": true, "description": "Mac character code (default: 0)." },
+                    { "name": "autoRepeat", "type": "boolean", "optional": true, "description": "Whether the event was generated from auto repeat (default: false)." },
+                    { "name": "isKeypad", "type": "boolean", "optional": true, "description": "Whether the event was generated from the keypad (default: false)." },
+                    { "name": "isSystemKey", "type": "boolean", "optional": true, "description": "Whether the event was a system key event (default: false)." }
+                ],
+                "description": "Dispatches a key event to the page."
+            },
+            {
+                "name": "dispatchMouseEvent",
+                "parameters": [
+                    { "name": "type", "type": "string", "enum": ["mousePressed", "mouseReleased", "mouseMoved"], "description": "Type of the mouse event." },
+                    { "name": "x", "type": "integer", "description": "X coordinate of the event relative to the main frame's viewport."},
+                    { "name": "y", "type": "integer", "description": "Y coordinate of the event relative to the main frame's viewport. 0 refers to the top of the viewport and Y increases as it proceeds towards the bottom of the viewport."},
+                    { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." },
+                    { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." },
+                    { "name": "button", "type": "string", "enum": ["none", "left", "middle", "right"], "optional": true, "description": "Mouse button (default: \"none\")." },
+                    { "name": "clickCount", "type": "integer", "optional": true, "description": "Number of times the mouse button was clicked (default: 0)." }
+                ],
+                "description": "Dispatches a mouse event to the page."
+            }
+        ],
+        "events": []
+    },
+    {
+        "domain": "LayerTree",
+        "hidden": true,
+        "types": [
+            {
+                "id": "LayerId",
+                "type": "string",
+                "description": "Unique RenderLayer identifier."
+            },
+            {
+                "id": "PseudoElementId",
+                "type": "string",
+                "description": "Unique PseudoElement identifier."
+            },
+            {
+                "id": "IntRect",
+                "type": "object",
+                "description": "A rectangle.",
+                "properties": [
+                    { "name": "x", "type": "integer", "description": "The x position." },
+                    { "name": "y", "type": "integer", "description": "The y position." },
+                    { "name": "width", "type": "integer", "description": "The width metric." },
+                    { "name": "height", "type": "integer", "description": "The height metric." }
+                ]
+            },
+            {
+                "id": "Layer",
+                "type": "object",
+                "description": "Information about a compositing layer.",
+                "properties": [
+                    { "name": "layerId", "$ref": "LayerId", "description": "The unique id for this layer." },
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The id for the node associated with this layer." },
+                    { "name": "bounds", "$ref": "IntRect", "description": "Bounds of the layer in absolute page coordinates." },
+                    { "name": "paintCount", "type": "integer", "description": "Indicates how many time this layer has painted." },
+                    { "name": "memory", "type": "integer", "description": "Estimated memory used by this layer." },
+                    { "name": "compositedBounds", "$ref": "IntRect", "description": "The bounds of the composited layer." },
+                    { "name": "isInShadowTree", "type": "boolean", "optional": true, "description": "Indicates whether this layer is associated with an element hosted in a shadow tree." },
+                    { "name": "isReflection", "type": "boolean", "optional": true, "description": "Indicates whether this layer was used to provide a reflection for the element." },
+                    { "name": "isGeneratedContent", "type": "boolean", "optional": true, "description": "Indicates whether the layer is attached to a pseudo element that is CSS generated content." },
+                    { "name": "pseudoElementId", "$ref": "PseudoElementId", "optional": true, "description": "The id for the pseudo element associated with this layer." },
+                    { "name": "pseudoClass", "type": "string", "optional": true, "description": "The name of the CSS pseudo-class that prompted the layer's content to be generated." }
+                ]
+            },
+            {
+                "id": "CompositingReasons",
+                "type": "object",
+                "description": "An object containing the reasons why the layer was composited as properties.",
+                "properties": [
+                    { "name": "transform3D", "type": "boolean", "optional": true, "description": "Composition due to association with an element with a CSS 3D transform." },
+                    { "name": "video", "type": "boolean", "optional": true, "description": "Composition due to association with a <video> element." },
+                    { "name": "canvas", "type": "boolean", "optional": true, "description": "Composition due to the element being a <canvas> element." },
+                    { "name": "plugin", "type": "boolean", "optional": true, "description": "Composition due to association with a plugin." },
+                    { "name": "iFrame", "type": "boolean", "optional": true, "description": "Composition due to association with an <iframe> element." },
+                    { "name": "backfaceVisibilityHidden", "type": "boolean", "optional": true, "description": "Composition due to association with an element with a \"backface-visibility: hidden\" style." },
+                    { "name": "clipsCompositingDescendants", "type": "boolean", "optional": true, "description": "Composition due to association with an element clipping compositing descendants." },
+                    { "name": "animation", "type": "boolean", "optional": true, "description": "Composition due to association with an animated element." },
+                    { "name": "filters", "type": "boolean", "optional": true, "description": "Composition due to association with an element with CSS filters applied." },
+                    { "name": "positionFixed", "type": "boolean", "optional": true, "description": "Composition due to association with an element with a \"position: fixed\" style." },
+                    { "name": "positionSticky", "type": "boolean", "optional": true, "description": "Composition due to association with an element with a \"position: sticky\" style." },
+                    { "name": "overflowScrollingTouch", "type": "boolean", "optional": true, "description": "Composition due to association with an element with a \"overflow-scrolling: touch\" style." },
+                    { "name": "stacking", "type": "boolean", "optional": true, "description": "Composition due to association with an element establishing a stacking context." },
+                    { "name": "overlap", "type": "boolean", "optional": true, "description": "Composition due to association with an element overlapping other composited elements." },
+                    { "name": "negativeZIndexChildren", "type": "boolean", "optional": true, "description": "Composition due to association with an element with descendants that have a negative z-index." },
+                    { "name": "transformWithCompositedDescendants", "type": "boolean", "optional": true, "description": "Composition due to association with an element with composited descendants." },
+                    { "name": "opacityWithCompositedDescendants", "type": "boolean", "optional": true, "description": "Composition due to association with an element with opacity applied and composited descendants." },
+                    { "name": "maskWithCompositedDescendants", "type": "boolean", "optional": true, "description": "Composition due to association with a masked element and composited descendants." },
+                    { "name": "reflectionWithCompositedDescendants", "type": "boolean", "optional": true, "description": "Composition due to association with an element with a reflection and composited descendants." },
+                    { "name": "filterWithCompositedDescendants", "type": "boolean", "optional": true, "description": "Composition due to association with an element with CSS filters applied and composited descendants." },
+                    { "name": "blendingWithCompositedDescendants", "type": "boolean", "optional": true, "description": "Composition due to association with an element with CSS blending applied and composited descendants." },
+                    { "name": "perspective", "type": "boolean", "optional": true, "description": "Composition due to association with an element with perspective applied." },
+                    { "name": "preserve3D", "type": "boolean", "optional": true, "description": "Composition due to association with an element with a \"transform-style: preserve-3d\" style." },
+                    { "name": "root", "type": "boolean", "optional": true, "description": "Composition due to association with the root element." }
+                ]
+            }
+        ],
+        "commands": [
+            {
+                "name": "enable",
+                "description": "Enables compositing tree inspection."
+            },
+            {
+                "name": "disable",
+                "description": "Disables compositing tree inspection."
+            },
+            {
+                "name": "layersForNode",
+                "parameters": [
+                    { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Root of the subtree for which we want to gather layers." }                ],
+                "description": "Returns the layer tree structure of the current page.",
+                "returns": [
+                    { "name": "layers", "type": "array", "items": { "$ref": "Layer" }, "description": "Child layers." }
+                ]
+            },
+            {
+                "name": "reasonsForCompositingLayer",
+                "parameters": [
+                    { "name": "layerId", "$ref": "LayerId", "description": "The id of the layer for which we want to get the reasons it was composited." }
+                ],
+                "description": "Provides the reasons why the given layer was composited.",
+                "returns": [
+                    { "name": "compositingReasons", "$ref": "CompositingReasons", "description": "An object containing the reasons why the layer was composited as properties." }
+                ]
+            }
+        ],
+        "events": [
+            {
+                "name": "layerTreeDidChange"
+            }
+        ]
+    }]
+}
diff --git a/Source/devtools/scripts/CodeGeneratorFrontend.py b/Source/devtools/scripts/CodeGeneratorFrontend.py
new file mode 100755
index 0000000..4ef4ef5
--- /dev/null
+++ b/Source/devtools/scripts/CodeGeneratorFrontend.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python
+# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Intel Corporation. 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.
+
+import os.path
+import sys
+import string
+import optparse
+import re
+try:
+    import json
+except ImportError:
+    import simplejson as json
+
+cmdline_parser = optparse.OptionParser()
+cmdline_parser.add_option("--output_js_dir")
+
+try:
+    arg_options, arg_values = cmdline_parser.parse_args()
+    if (len(arg_values) != 1):
+        raise Exception("Exactly one plain argument expected (found %s)" % len(arg_values))
+    input_json_filename = arg_values[0]
+    output_js_dirname = arg_options.output_js_dir
+    if not output_js_dirname:
+        raise Exception("Output .js directory must be specified")
+except Exception:
+    # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
+    exc = sys.exc_info()[1]
+    sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc)
+    sys.stderr.write("Usage: <script> protocol.json --output_js_dir <output_js_dir>\n")
+    exit(1)
+
+
+def fix_camel_case(name):
+    refined = re.sub(r'-(\w)', lambda pat: pat.group(1).upper(), name)
+    refined = to_title_case(refined)
+    return re.sub(r'(?i)HTML|XML|WML|API', lambda pat: pat.group(0).upper(), refined)
+
+
+def to_title_case(name):
+    return name[:1].upper() + name[1:]
+
+
+class RawTypes(object):
+    @staticmethod
+    def get_js(json_type):
+        if json_type == "boolean":
+            return "boolean"
+        elif json_type == "string":
+            return "string"
+        elif json_type == "array":
+            return "object"
+        elif json_type == "object":
+            return "object"
+        elif json_type == "integer":
+            return "number"
+        elif json_type == "number":
+            return "number"
+        elif json_type == "any":
+            raise Exception("Unsupported")
+        else:
+            raise Exception("Unknown type: %s" % json_type)
+
+
+class TypeData(object):
+    def __init__(self, json_type):
+        if "type" not in json_type:
+            raise Exception("Unknown type")
+        json_type_name = json_type["type"]
+        self.raw_type_js_ = RawTypes.get_js(json_type_name)
+
+    def get_raw_type_js(self):
+        return self.raw_type_js_
+
+
+class TypeMap:
+    def __init__(self, api):
+        self.map_ = {}
+        for json_domain in api["domains"]:
+            domain_name = json_domain["domain"]
+
+            domain_map = {}
+            self.map_[domain_name] = domain_map
+
+            if "types" in json_domain:
+                for json_type in json_domain["types"]:
+                    type_name = json_type["id"]
+                    type_data = TypeData(json_type)
+                    domain_map[type_name] = type_data
+
+    def get(self, domain_name, type_name):
+        return self.map_[domain_name][type_name]
+
+
+def resolve_param_raw_type_js(json_parameter, scope_domain_name):
+    if "$ref" in json_parameter:
+        json_ref = json_parameter["$ref"]
+        return get_ref_data_js(json_ref, scope_domain_name)
+    elif "type" in json_parameter:
+        json_type = json_parameter["type"]
+        return RawTypes.get_js(json_type)
+    else:
+        raise Exception("Unknown type")
+
+
+def get_ref_data_js(json_ref, scope_domain_name):
+    dot_pos = json_ref.find(".")
+    if dot_pos == -1:
+        domain_name = scope_domain_name
+        type_name = json_ref
+    else:
+        domain_name = json_ref[:dot_pos]
+        type_name = json_ref[dot_pos + 1:]
+
+    return type_map.get(domain_name, type_name).get_raw_type_js()
+
+
+input_file = open(input_json_filename, "r")
+json_string = input_file.read()
+json_api = json.loads(json_string)
+
+
+class Templates:
+    def get_this_script_path_(absolute_path):
+        absolute_path = os.path.abspath(absolute_path)
+        components = []
+
+        def fill_recursive(path_part, depth):
+            if depth <= 0 or path_part == '/':
+                return
+            fill_recursive(os.path.dirname(path_part), depth - 1)
+            components.append(os.path.basename(path_part))
+
+        # Typical path is /Source/WebCore/inspector/CodeGeneratorInspector.py
+        # Let's take 4 components from the real path then.
+        fill_recursive(absolute_path, 4)
+
+        return "/".join(components)
+
+    file_header_ = ("// File is generated by %s\n\n" % get_this_script_path_(sys.argv[0]) +
+"""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+""")
+
+    backend_js = string.Template(file_header_ + """
+
+$domainInitializers
+""")
+
+
+type_map = TypeMap(json_api)
+
+
+class Generator:
+    backend_js_domain_initializer_list = []
+
+    @staticmethod
+    def go():
+        for json_domain in json_api["domains"]:
+            domain_name = json_domain["domain"]
+            domain_name_lower = domain_name.lower()
+
+            Generator.backend_js_domain_initializer_list.append("// %s.\n" % domain_name)
+            Generator.backend_js_domain_initializer_list.append("InspectorBackend.register%sDispatcher = InspectorBackend.registerDomainDispatcher.bind(InspectorBackend, \"%s\");\n" % (domain_name, domain_name))
+
+            if "types" in json_domain:
+                for json_type in json_domain["types"]:
+                    if "type" in json_type and json_type["type"] == "string" and "enum" in json_type:
+                        enum_name = "%s.%s" % (domain_name, json_type["id"])
+                        Generator.process_enum(json_type, enum_name)
+                    elif json_type["type"] == "object":
+                        if "properties" in json_type:
+                            for json_property in json_type["properties"]:
+                                if "type" in json_property and json_property["type"] == "string" and "enum" in json_property:
+                                    enum_name = "%s.%s%s" % (domain_name, json_type["id"], to_title_case(json_property["name"]))
+                                    Generator.process_enum(json_property, enum_name)
+
+            if "events" in json_domain:
+                for json_event in json_domain["events"]:
+                    Generator.process_event(json_event, domain_name)
+
+            if "commands" in json_domain:
+                for json_command in json_domain["commands"]:
+                    Generator.process_command(json_command, domain_name)
+
+            Generator.backend_js_domain_initializer_list.append("\n")
+
+    @staticmethod
+    def process_enum(json_enum, enum_name):
+        enum_members = []
+        for member in json_enum["enum"]:
+            enum_members.append("%s: \"%s\"" % (fix_camel_case(member), member))
+
+        Generator.backend_js_domain_initializer_list.append("InspectorBackend.registerEnum(\"%s\", {%s});\n" % (
+            enum_name, ", ".join(enum_members)))
+
+    @staticmethod
+    def process_event(json_event, domain_name):
+        event_name = json_event["name"]
+
+        json_parameters = json_event.get("parameters")
+
+        backend_js_event_param_list = []
+        if json_parameters:
+            for parameter in json_parameters:
+                parameter_name = parameter["name"]
+                backend_js_event_param_list.append("\"%s\"" % parameter_name)
+
+        Generator.backend_js_domain_initializer_list.append("InspectorBackend.registerEvent(\"%s.%s\", [%s]);\n" % (
+            domain_name, event_name, ", ".join(backend_js_event_param_list)))
+
+    @staticmethod
+    def process_command(json_command, domain_name):
+        json_command_name = json_command["name"]
+
+        js_parameters_text = ""
+        if "parameters" in json_command:
+            json_params = json_command["parameters"]
+            js_param_list = []
+
+            for json_parameter in json_params:
+                json_param_name = json_parameter["name"]
+                js_bind_type = resolve_param_raw_type_js(json_parameter, domain_name)
+
+                optional = json_parameter.get("optional")
+
+
+                js_param_text = "{\"name\": \"%s\", \"type\": \"%s\", \"optional\": %s}" % (
+                    json_param_name,
+                    js_bind_type,
+                    ("true" if ("optional" in json_parameter and json_parameter["optional"]) else "false"))
+
+                js_param_list.append(js_param_text)
+
+            js_parameters_text = ", ".join(js_param_list)
+
+
+        backend_js_reply_param_list = []
+        if "returns" in json_command:
+            for json_return in json_command["returns"]:
+                json_return_name = json_return["name"]
+                backend_js_reply_param_list.append("\"%s\"" % json_return_name)
+
+        js_reply_list = "[%s]" % ", ".join(backend_js_reply_param_list)
+
+        Generator.backend_js_domain_initializer_list.append("InspectorBackend.registerCommand(\"%s.%s\", [%s], %s);\n" % (domain_name, json_command_name, js_parameters_text, js_reply_list))
+
+Generator.go()
+
+backend_js_file = open(output_js_dirname + "/InspectorBackendCommands.js", "w")
+
+backend_js_file.write(Templates.backend_js.substitute(None,
+    domainInitializers="".join(Generator.backend_js_domain_initializer_list)))
+
+backend_js_file.close()
diff --git a/Source/devtools/scripts/closure/COPYING b/Source/devtools/scripts/closure/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Source/devtools/scripts/closure/COPYING
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/Source/devtools/scripts/closure/README b/Source/devtools/scripts/closure/README
new file mode 100644
index 0000000..14fc8fe
--- /dev/null
+++ b/Source/devtools/scripts/closure/README
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2009 The Closure Compiler Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Contents
+//
+
+The Closure Compiler performs checking, instrumentation, and
+optimizations on JavaScript code. The purpose of this README is to
+explain how to build and run the Closure Compiler.
+
+The Closure Compiler requires Java 6 or higher.
+http://www.java.com/
+
+
+//
+// Building The Closure Compiler
+//
+
+There are three ways to get a Closure Compiler executable.
+
+1) Use one we built for you.
+
+Pre-built Closure binaries can be found at
+http://code.google.com/p/closure-compiler/downloads/list
+
+
+2) Check out the source and build it with Apache Ant.
+
+First, check out the full source tree of the Closure Compiler. There
+are instructions on how to do this at the project site.
+http://code.google.com/p/closure-compiler/source/checkout
+
+Apache Ant is a cross-platform build tool.
+http://ant.apache.org/
+
+At the root of the source tree, there is an Ant file named
+build.xml. To use it, navigate to the same directory and type the
+command
+
+ant jar
+
+This will produce a jar file called "build/compiler.jar".
+
+
+3) Check out the source and build it with Eclipse.
+
+Eclipse is a cross-platform IDE.
+http://www.eclipse.org/
+
+Under Eclipse's File menu, click "New > Project ..." and create a
+"Java Project."  You will see an options screen. Give the project a
+name, select "Create project from existing source," and choose the
+root of the checked-out source tree as the existing directory. Verify
+that you are using JRE version 6 or higher.
+
+Eclipse can use the build.xml file to discover rules. When you
+navigate to the build.xml file, you will see all the build rules in
+the "Outline" pane. Run the "jar" rule to build the compiler in
+build/compiler.jar.
+
+
+//
+// Running The Closure Compiler
+//
+
+Once you have the jar binary, running the Closure Compiler is straightforward.
+
+On the command line, type
+
+java -jar compiler.jar
+
+This starts the compiler in interactive mode. Type
+
+var x = 17 + 25;
+
+then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux)
+and "Enter" again. The Compiler will respond:
+
+var x=42;
+
+The Closure Compiler has many options for reading input from a file,
+writing output to a file, checking your code, and running
+optimizations. To learn more, type
+
+java -jar compiler.jar --help
+
+You can read more detailed documentation about the many flags at
+http://code.google.com/closure/compiler/docs/gettingstarted_app.html
+
+
+//
+// Compiling Multiple Scripts
+//
+
+If you have multiple scripts, you should compile them all together with
+one compile command.
+
+java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js
+
+The Closure Compiler will concatenate the files in the order they're
+passed at the command line.
+
+If you need to compile many, many scripts together, you may start to
+run into problems with managing dependencies between scripts. You
+should check out the Closure Library. It contains functions for
+enforcing dependencies between scripts, and a tool called calcdeps.py
+that knows how to give scripts to the Closure Compiler in the right
+order.
+
+http://code.google.com/p/closure-library/
+
+//
+// Licensing
+//
+
+Unless otherwise stated, all source files are licensed under
+the Apache License, Version 2.0.
+
+
+-----
+Code under:
+src/com/google/javascript/rhino
+test/com/google/javascript/rhino
+
+URL: http://www.mozilla.org/rhino
+Version:  1.5R3, with heavy modifications
+License:  Netscape Public License and MPL / GPL dual license
+
+Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an
+implementation of JavaScript for the JVM.  The JavaScript parser and
+the parse tree data structures were extracted and modified
+significantly for use by Google's JavaScript compiler.
+
+Local Modifications: The packages have been renamespaced. All code not
+relevant to parsing has been removed. A JsDoc parser and static typing
+system have been added.
+
+
+-----
+Code in:
+lib/rhino
+
+Rhino
+URL: http://www.mozilla.org/rhino
+Version:  Trunk
+License:  Netscape Public License and MPL / GPL dual license
+
+Description: Mozilla Rhino is an implementation of JavaScript for the JVM.
+
+Local Modifications: Minor changes to parsing JSDoc that usually get pushed
+up-stream to Rhino trunk.
+
+
+-----
+Code in:
+lib/args4j.jar
+
+Args4j
+URL: https://args4j.dev.java.net/
+Version: 2.0.16
+License: MIT
+
+Description:
+args4j is a small Java class library that makes it easy to parse command line
+options/arguments in your CUI application.
+
+Local Modifications: None.
+
+
+-----
+Code in:
+lib/guava.jar
+
+Guava Libraries
+URL: http://code.google.com/p/guava-libraries/
+Version:  14.0
+License: Apache License 2.0
+
+Description: Google's core Java libraries.
+
+Local Modifications: None.
+
+
+-----
+Code in:
+lib/jsr305.jar
+
+Annotations for software defect detection
+URL: http://code.google.com/p/jsr-305/
+Version: svn revision 47
+License: BSD License
+
+Description: Annotations for software defect detection.
+
+Local Modifications: None.
+
+
+-----
+Code in:
+lib/jarjar.jar
+
+Jar Jar Links
+URL: http://jarjar.googlecode.com/
+Version: 1.1
+License: Apache License 2.0
+
+Description:
+A utility for repackaging Java libraries.
+
+Local Modifications: None.
+
+
+----
+Code in:
+lib/junit.jar
+
+JUnit
+URL:  http://sourceforge.net/projects/junit/
+Version:  4.10
+License:  Common Public License 1.0
+
+Description: A framework for writing and running automated tests in Java.
+
+Local Modifications: None.
+
+
+---
+Code in:
+lib/protobuf-java.jar
+
+Protocol Buffers
+URL: http://code.google.com/p/protobuf/
+Version: 2.4.1
+License: New BSD License
+
+Description: Supporting libraries for protocol buffers,
+an encoding of structured data.
+
+Local Modifications: None
+
+
+---
+Code in:
+lib/ant.jar
+lib/ant-launcher.jar
+
+URL: http://ant.apache.org/bindownload.cgi
+Version: 1.8.1
+License: Apache License 2.0
+Description:
+  Ant is a Java based build tool. In theory it is kind of like "make"
+  without make's wrinkles and with the full portability of pure java code.
+
+Local Modifications: None
+
+
+---
+Code in:
+lib/json.jar
+URL: http://json.org/java/index.html
+Version: JSON version 20090211
+License: MIT license
+Description:
+JSON is a set of java files for use in transmitting data in JSON format.
+
+Local Modifications: None
+
+---
+Code in:
+tools/maven-ant-tasks-2.1.3.jar
+URL: http://maven.apache.org
+Version 2.1.3
+License: Apache License 2.0
+Description:
+  Maven Ant tasks are used to manage dependencies and to install/deploy to
+  maven repositories.
+
+Local Modifications: None
diff --git a/Source/devtools/scripts/closure/compiler.jar b/Source/devtools/scripts/closure/compiler.jar
new file mode 100644
index 0000000..e233867
--- /dev/null
+++ b/Source/devtools/scripts/closure/compiler.jar
Binary files differ
diff --git a/Source/devtools/scripts/compile_frontend.py b/Source/devtools/scripts/compile_frontend.py
new file mode 100755
index 0000000..6522083
--- /dev/null
+++ b/Source/devtools/scripts/compile_frontend.py
@@ -0,0 +1,452 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 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:
+#
+#     * 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.
+
+import os
+import os.path
+import generate_protocol_externs
+import shutil
+import sys
+import tempfile
+
+scripts_path = os.path.dirname(os.path.abspath(__file__))
+devtools_path = os.path.dirname(scripts_path)
+inspector_path = os.path.dirname(devtools_path) + "/core/inspector"
+devtools_frontend_path = devtools_path + "/front_end"
+protocol_externs_path = devtools_frontend_path + "/protocol_externs.js"
+
+generate_protocol_externs.generate_protocol_externs(protocol_externs_path, devtools_path + "/protocol.json")
+
+jsmodule_name_prefix = "jsmodule_"
+modules = [
+    {
+        "name": "common",
+        "dependencies": [],
+        "sources": [
+            "Color.js",
+            "DOMExtension.js",
+            "Object.js",
+            "ParsedURL.js",
+            "Progress.js",
+            "Settings.js",
+            "UIString.js",
+            "UserMetrics.js",
+            "utilities.js",
+        ]
+    },
+    {
+        "name": "sdk",
+        "dependencies": ["common"],
+        "sources": [
+            "ApplicationCacheModel.js",
+            "CompilerScriptMapping.js",
+            "ConsoleModel.js",
+            "ContentProvider.js",
+            "ContentProviderBasedProjectDelegate.js",
+            "ContentProviders.js",
+            "CookieParser.js",
+            "CSSMetadata.js",
+            "CSSStyleModel.js",
+            "BreakpointManager.js",
+            "Database.js",
+            "DOMAgent.js",
+            "DOMStorage.js",
+            "DebuggerModel.js",
+            "DebuggerScriptMapping.js",
+            "FileManager.js",
+            "FileMapping.js",
+            "FileSystemMapping.js",
+            "FileSystemModel.js",
+            "FileSystemProjectDelegate.js",
+            "FileUtils.js",
+            "HAREntry.js",
+            "IndexedDBModel.js",
+            "InspectorBackend.js",
+            "IsolatedFileSystemManager.js",
+            "IsolatedFileSystem.js",
+            "Linkifier.js",
+            "NetworkLog.js",
+            "NetworkUISourceCodeProvider.js",
+            "PresentationConsoleMessageHelper.js",
+            "RuntimeModel.js",
+            "SASSSourceMapping.js",
+            "Script.js",
+            "ScriptFormatter.js",
+            "ScriptSnippetModel.js",
+            "SimpleWorkspaceProvider.js",
+            "SnippetStorage.js",
+            "SourceMapping.js",
+            "StylesSourceMapping.js",
+            "TimelineManager.js",
+            "RemoteObject.js",
+            "Resource.js",
+            "DefaultScriptMapping.js",
+            "ResourceScriptMapping.js",
+            "LiveEditSupport.js",
+            "ResourceTreeModel.js",
+            "ResourceType.js",
+            "ResourceUtils.js",
+            "SourceMap.js",
+            "NetworkManager.js",
+            "NetworkRequest.js",
+            "UISourceCode.js",
+            "UserAgentSupport.js",
+            "Workspace.js",
+            "protocol_externs.js",
+        ]
+    },
+    {
+        "name": "ui",
+        "dependencies": ["common"],
+        "sources": [
+            "Checkbox.js",
+            "CodeMirrorTextEditor.js",
+            "ContextMenu.js",
+            "DOMSyntaxHighlighter.js",
+            "DataGrid.js",
+            "DefaultTextEditor.js",
+            "Dialog.js",
+            "DockController.js",
+            "Drawer.js",
+            "EmptyView.js",
+            "GoToLineDialog.js",
+            "HelpScreen.js",
+            "InspectorView.js",
+            "KeyboardShortcut.js",
+            "OverviewGrid.js",
+            "Panel.js",
+            "Placard.js",
+            "Popover.js",
+            "ProgressIndicator.js",
+            "PropertiesSection.js",
+            "SearchController.js",
+            "Section.js",
+            "SidebarPane.js",
+            "SidebarTreeElement.js",
+            "ShortcutsScreen.js",
+            "ShowMoreDataGridNode.js",
+            "SidebarOverlay.js",
+            "SoftContextMenu.js",
+            "SourceTokenizer.js",
+            "Spectrum.js",
+            "SplitView.js",
+            "SidebarView.js",
+            "StatusBarButton.js",
+            "SuggestBox.js",
+            "TabbedPane.js",
+            "TextEditor.js",
+            "TextEditorHighlighter.js",
+            "TextEditorModel.js",
+            "TextPrompt.js",
+            "TextUtils.js",
+            "TimelineGrid.js",
+            "Toolbar.js",
+            "UIUtils.js",
+            "View.js",
+            "ViewportControl.js",
+            "treeoutline.js",
+        ]
+    },
+    {
+        "name": "components",
+        "dependencies": ["sdk", "ui"],
+        "sources": [
+            "AdvancedSearchController.js",
+            "HandlerRegistry.js",
+            "ConsoleMessage.js",
+            "CookiesTable.js",
+            "DOMBreakpointsSidebarPane.js",
+            "DOMPresentationUtils.js",
+            "ElementsTreeOutline.js",
+            "FontView.js",
+            "ImageView.js",
+            "NativeBreakpointsSidebarPane.js",
+            "InspectElementModeController.js",
+            "ObjectPopoverHelper.js",
+            "ObjectPropertiesSection.js",
+            "SourceFrame.js",
+            "ResourceView.js",
+        ]
+    },
+    {
+        "name": "elements",
+        "dependencies": ["components"],
+        "sources": [
+            "CSSNamedFlowCollectionsView.js",
+            "CSSNamedFlowView.js",
+            "ElementsPanel.js",
+            "ElementsPanelDescriptor.js",
+            "EventListenersSidebarPane.js",
+            "MetricsSidebarPane.js",
+            "PropertiesSidebarPane.js",
+            "StylesSidebarPane.js",
+        ]
+    },
+    {
+        "name": "network",
+        "dependencies": ["components"],
+        "sources": [
+            "NetworkItemView.js",
+            "RequestCookiesView.js",
+            "RequestHeadersView.js",
+            "RequestHTMLView.js",
+            "RequestJSONView.js",
+            "RequestPreviewView.js",
+            "RequestResponseView.js",
+            "RequestTimingView.js",
+            "RequestView.js",
+            "ResourceWebSocketFrameView.js",
+            "NetworkPanel.js",
+            "NetworkPanelDescriptor.js",
+        ]
+    },
+    {
+        "name": "resources",
+        "dependencies": ["components"],
+        "sources": [
+            "ApplicationCacheItemsView.js",
+            "CookieItemsView.js",
+            "DatabaseQueryView.js",
+            "DatabaseTableView.js",
+            "DirectoryContentView.js",
+            "DOMStorageItemsView.js",
+            "FileContentView.js",
+            "FileSystemView.js",
+            "IndexedDBViews.js",
+            "ResourcesPanel.js",
+        ]
+    },
+    {
+        "name": "workers",
+        "dependencies": ["components"],
+        "sources": [
+            "WorkerManager.js",
+        ]
+    },
+    {
+        "name": "scripts",
+        "dependencies": ["components", "workers"],
+        "sources": [
+            "BreakpointsSidebarPane.js",
+            "CallStackSidebarPane.js",
+            "FilteredItemSelectionDialog.js",
+            "JavaScriptSourceFrame.js",
+            "NavigatorOverlayController.js",
+            "NavigatorView.js",
+            "RevisionHistoryView.js",
+            "ScopeChainSidebarPane.js",
+            "ScriptsNavigator.js",
+            "ScriptsPanel.js",
+            "ScriptsPanelDescriptor.js",
+            "ScriptsSearchScope.js",
+            "StyleSheetOutlineDialog.js",
+            "TabbedEditorContainer.js",
+            "UISourceCodeFrame.js",
+            "WatchExpressionsSidebarPane.js",
+            "WorkersSidebarPane.js",
+        ]
+    },
+    {
+        "name": "console",
+        "dependencies": ["components"],
+        "sources": [
+            "ConsoleView.js",
+            "ConsolePanel.js",
+        ]
+    },
+    {
+        "name": "timeline",
+        "dependencies": ["components"],
+        "sources": [
+            "DOMCountersGraph.js",
+            "MemoryStatistics.js",
+            "NativeMemoryGraph.js",
+            "TimelineModel.js",
+            "TimelineOverviewPane.js",
+            "TimelinePanel.js",
+            "TimelinePanelDescriptor.js",
+            "TimelinePresentationModel.js",
+            "TimelineFrameController.js"
+        ]
+    },
+    {
+        "name": "audits",
+        "dependencies": ["components"],
+        "sources": [
+            "AuditCategories.js",
+            "AuditController.js",
+            "AuditFormatters.js",
+            "AuditLauncherView.js",
+            "AuditResultView.js",
+            "AuditRules.js",
+            "AuditsPanel.js",
+        ]
+    },
+    {
+        "name": "extensions",
+        "dependencies": ["components"],
+        "sources": [
+            "ExtensionAPI.js",
+            "ExtensionAuditCategory.js",
+            "ExtensionPanel.js",
+            "ExtensionRegistryStub.js",
+            "ExtensionServer.js",
+            "ExtensionView.js",
+        ]
+    },
+    {
+        "name": "settings",
+        "dependencies": ["components", "extensions"],
+        "sources": [
+            "SettingsScreen.js",
+            "OverridesView.js",
+        ]
+    },
+    {
+        "name": "tests",
+        "dependencies": ["components"],
+        "sources": [
+            "TestController.js",
+        ]
+    },
+    {
+        "name": "profiler",
+        "dependencies": ["components", "workers"],
+        "sources": [
+            "BottomUpProfileDataGridTree.js",
+            "CPUProfileView.js",
+            "CSSSelectorProfileView.js",
+            "FlameChart.js",
+            "HeapSnapshot.js",
+            "HeapSnapshotDataGrids.js",
+            "HeapSnapshotGridNodes.js",
+            "HeapSnapshotLoader.js",
+            "HeapSnapshotProxy.js",
+            "HeapSnapshotView.js",
+            "HeapSnapshotWorker.js",
+            "HeapSnapshotWorkerDispatcher.js",
+            "JSHeapSnapshot.js",
+            "NativeHeapSnapshot.js",
+            "NativeMemorySnapshotView.js",
+            "ProfileDataGridTree.js",
+            "ProfilesPanel.js",
+            "ProfilesPanelDescriptor.js",
+            "ProfileLauncherView.js",
+            "TopDownProfileDataGridTree.js",
+            "CanvasProfileView.js",
+        ]
+    },
+    {
+        "name": "host_stub",
+        "dependencies": ["components", "profiler", "timeline"],
+        "sources": [
+            "InspectorFrontendAPI.js",
+            "InspectorFrontendHostStub.js",
+        ]
+    }
+]
+
+modules_by_name = {}
+for module in modules:
+    modules_by_name[module["name"]] = module
+
+
+def dump_module(name, recursively, processed_modules):
+    if name in processed_modules:
+        return ""
+    processed_modules[name] = True
+    module = modules_by_name[name]
+    command = ""
+    if recursively:
+        for dependency in module["dependencies"]:
+            command += dump_module(dependency, recursively, processed_modules)
+    command += " \\\n    --module " + jsmodule_name_prefix + module["name"] + ":"
+    command += str(len(module["sources"]))
+    firstDependency = True
+    for dependency in module["dependencies"]:
+        if firstDependency:
+            command += ":"
+        else:
+            command += ","
+        firstDependency = False
+        command += jsmodule_name_prefix + dependency
+    for script in module["sources"]:
+        command += " \\\n        --js " + devtools_frontend_path + "/" + script
+    return command
+
+modules_dir = tempfile.mkdtemp()
+compiler_command = "java -jar %s/closure/compiler.jar --summary_detail_level 3 --compilation_level SIMPLE_OPTIMIZATIONS \
+    --warning_level VERBOSE --jscomp_off=es5Strict --language_in ECMASCRIPT5 --accept_const_keyword --module_output_path_prefix %s/ \\\n" % (scripts_path, modules_dir)
+
+process_recursively = len(sys.argv) > 1
+if process_recursively:
+    module_name = sys.argv[1]
+    if module_name != "all":
+        modules = []
+        for i in range(1, len(sys.argv)):
+            modules.append(modules_by_name[sys.argv[i]])
+    for module in modules:
+        command = compiler_command
+        command += "    --externs " + devtools_frontend_path + "/externs.js"
+        command += dump_module(module["name"], True, {})
+        print "Compiling \"" + module["name"] + "\""
+        os.system(command)
+else:
+    command = compiler_command
+    command += "    --externs " + devtools_frontend_path + "/externs.js"
+    for module in modules:
+        command += dump_module(module["name"], False, {})
+    os.system(command)
+
+if not process_recursively:
+    print "Compiling InjectedScriptSource.js..."
+    os.system("echo \"var injectedScriptValue = \" > " + inspector_path + "/" + "InjectedScriptSourceTmp.js")
+    os.system("cat  " + inspector_path + "/" + "InjectedScriptSource.js" + " >> " + inspector_path + "/" + "InjectedScriptSourceTmp.js")
+    command = compiler_command
+    command += "    --externs " + inspector_path + "/" + "InjectedScriptExterns.js" + " \\\n"
+    command += "    --externs " + protocol_externs_path + " \\\n"
+    command += "    --module " + jsmodule_name_prefix + "injected_script" + ":" + "1" + " \\\n"
+    command += "        --js " + inspector_path + "/" + "InjectedScriptSourceTmp.js" + " \\\n"
+    command += "\n"
+    os.system(command)
+    os.system("rm " + inspector_path + "/" + "InjectedScriptSourceTmp.js")
+
+    print "Compiling InjectedScriptCanvasModuleSource.js..."
+    os.system("echo \"var injectedScriptCanvasModuleValue = \" > " + inspector_path + "/" + "InjectedScriptCanvasModuleSourceTmp.js")
+    os.system("cat  " + inspector_path + "/" + "InjectedScriptCanvasModuleSource.js" + " >> " + inspector_path + "/" + "InjectedScriptCanvasModuleSourceTmp.js")
+    command = compiler_command
+    command += "    --externs " + inspector_path + "/" + "InjectedScriptExterns.js" + " \\\n"
+    command += "    --externs " + protocol_externs_path + " \\\n"
+    command += "    --module " + jsmodule_name_prefix + "injected_script" + ":" + "1" + " \\\n"
+    command += "        --js " + inspector_path + "/" + "InjectedScriptCanvasModuleSourceTmp.js" + " \\\n"
+    command += "\n"
+    os.system(command)
+    os.system("rm " + inspector_path + "/" + "InjectedScriptCanvasModuleSourceTmp.js")
+
+shutil.rmtree(modules_dir)
+#os.system("rm " + protocol_externs_path)
diff --git a/Source/devtools/scripts/concatenate_css_files.py b/Source/devtools/scripts/concatenate_css_files.py
new file mode 100644
index 0000000..e6c0367
--- /dev/null
+++ b/Source/devtools/scripts/concatenate_css_files.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2010 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:
+#
+#         * 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.
+#
+
+# This script concatenates in place CSS files in the order specified
+# using <link> tags in a given 'order.html' file.
+
+from __future__ import with_statement
+
+from HTMLParser import HTMLParser
+from cStringIO import StringIO
+import os.path
+import sys
+
+
+class OrderedCSSFilesExtractor(HTMLParser):
+
+    def __init__(self, order_html):
+        HTMLParser.__init__(self)
+        self.ordered_css_files = []
+        self.feed(order_html)
+
+    def handle_starttag(self, tag, attrs):
+        if tag == 'link':
+            attrs_dict = dict(attrs)
+            if ('type' in attrs_dict and attrs_dict['type'] == 'text/css' and
+                'href' in attrs_dict):
+                self.ordered_css_files.append(attrs_dict['href'])
+
+
+class PathExpander:
+
+    def __init__(self, paths):
+        self.paths = paths
+
+    def expand(self, filename):
+        last_path = None
+        expanded_name = None
+        for path in self.paths:
+            fname = "%s/%s" % (path, filename)
+            if (os.access(fname, os.F_OK)):
+                if (last_path != None):
+                    raise Exception('Ambiguous file %s: found in %s and %s' %
+                                    (filename, last_path, path))
+                expanded_name = fname
+                last_path = path
+        return expanded_name
+
+
+def main(argv):
+
+    if len(argv) < 3:
+        print('usage: %s order.html input_source_dir_1 input_source_dir_2 ... '
+              'output_file' % argv[0])
+        return 1
+
+    output_file_name = argv.pop()
+    input_order_file_name = argv[1]
+    with open(input_order_file_name, 'r') as order_html:
+        extractor = OrderedCSSFilesExtractor(order_html.read())
+    # Unconditionally append inspector.css. It will contain concatenated files.
+    extractor.ordered_css_files.append('inspector.css')
+
+    expander = PathExpander(argv[2:])
+    output = StringIO()
+
+    for input_file_name in extractor.ordered_css_files:
+        full_path = expander.expand(input_file_name)
+        if (full_path is None):
+            raise Exception('File %s referenced in %s not found on any source paths, '
+                            'check source tree for consistency' %
+                            (input_file_name, input_order_file_name))
+        output.write('/* %s */\n\n' % input_file_name)
+        input_file = open(full_path, 'r')
+        output.write(input_file.read())
+        output.write('\n')
+        input_file.close()
+
+    if os.path.exists(output_file_name):
+        os.remove(output_file_name);
+    output_file = open(output_file_name, 'w')
+    output_file.write(output.getvalue())
+    output_file.close()
+    output.close()
+
+    # Touch output file directory to make sure that Xcode will copy
+    # modified resource files.
+    if sys.platform == 'darwin':
+        output_dir_name = os.path.dirname(output_file_name)
+        os.utime(output_dir_name, None)
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
diff --git a/Source/devtools/scripts/concatenate_js_files.py b/Source/devtools/scripts/concatenate_js_files.py
new file mode 100644
index 0000000..5d212c7
--- /dev/null
+++ b/Source/devtools/scripts/concatenate_js_files.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2010 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:
+#
+#         * 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.
+#
+
+# This script concatenates in place JS files in the order specified
+# using <script> tags in a given 'order.html' file.
+
+from __future__ import with_statement
+
+from HTMLParser import HTMLParser
+from cStringIO import StringIO
+
+import jsmin
+import os.path
+import sys
+
+
+class OrderedJSFilesExtractor(HTMLParser):
+
+    def __init__(self, order_html):
+        HTMLParser.__init__(self)
+        self.ordered_js_files = []
+        self.feed(order_html)
+
+    def handle_starttag(self, tag, attrs):
+        if tag == 'script':
+            attrs_dict = dict(attrs)
+            if ('type' in attrs_dict and attrs_dict['type'] == 'text/javascript' and 'src' in attrs_dict):
+                self.ordered_js_files.append(attrs_dict['src'])
+
+class PathExpander:
+
+    def __init__(self, paths):
+        self.paths = paths
+
+    def expand(self, filename):
+        last_path = None
+        expanded_name = None
+        for path in self.paths:
+            fname = "%s/%s" % (path, filename)
+            if (os.access(fname, os.F_OK)):
+                if (last_path != None):
+                    raise Exception('Ambiguous file %s: found in %s and %s' %
+                                    (filename, last_path, path))
+                expanded_name = fname
+                last_path = path
+        return expanded_name
+
+
+def main(argv):
+
+    if len(argv) < 3:
+        print('usage: %s order.html input_source_dir_1 input_source_dir_2 ... '
+              'output_file' % argv[0])
+        return 1
+
+    output_file_name = argv.pop()
+    input_order_file_name = argv[1]
+    with open(input_order_file_name, 'r') as order_html:
+        extractor = OrderedJSFilesExtractor(order_html.read())
+
+    expander = PathExpander(argv[2:])
+    output = StringIO()
+
+    for input_file_name in extractor.ordered_js_files:
+        full_path = expander.expand(input_file_name)
+        if (full_path is None):
+            raise Exception('File %s referenced in %s not found on any source paths, '
+                            'check source tree for consistency' %
+                            (input_file_name, input_order_file_name))
+        output.write('/* %s */\n\n' % input_file_name)
+        input_file = open(full_path, 'r')
+        output.write(input_file.read())
+        output.write('\n')
+        input_file.close()
+
+    if os.path.exists(output_file_name):
+        os.remove(output_file_name)
+    output_file = open(output_file_name, 'w')
+    output_file.write(jsmin.jsmin(output.getvalue()))
+    output_file.close()
+    output.close()
+
+    # Touch output file directory to make sure that Xcode will copy
+    # modified resource files.
+    if sys.platform == 'darwin':
+        output_dir_name = os.path.dirname(output_file_name)
+        os.utime(output_dir_name, None)
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
diff --git a/Source/devtools/scripts/generate_devtools_extension_api.py b/Source/devtools/scripts/generate_devtools_extension_api.py
new file mode 100644
index 0000000..d6d9826
--- /dev/null
+++ b/Source/devtools/scripts/generate_devtools_extension_api.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+#
+# 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:
+#
+#         * 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.
+#
+
+import sys
+
+
+def write_devtools_extension_api(output, input_names):
+    output.write("""(function() {
+    """)
+    for input_name in input_names:
+        input = open(input_name, 'r')
+        output.write(input.read())
+    output.write("""
+        var tabId;
+        var extensionInfo = {};
+        platformExtensionAPI(injectedExtensionAPI("remote-" + window.parent.frames.length));
+    })();""")
+
+
+def main(argv):
+
+    if len(argv) < 3:
+        print('usage: %s output_js input_files ...' % argv[0])
+        return 1
+
+    output_name = argv[1]
+    output = open(output_name, 'w')
+    write_devtools_extension_api(output, argv[2:])
+    output.close()
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
diff --git a/Source/devtools/scripts/generate_devtools_grd.py b/Source/devtools/scripts/generate_devtools_grd.py
new file mode 100644
index 0000000..fabe64b
--- /dev/null
+++ b/Source/devtools/scripts/generate_devtools_grd.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+#
+# 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:
+#
+#         * 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.
+
+"""Creates a grd file for packaging the inspector files."""
+
+from __future__ import with_statement
+
+import errno
+import os
+import shutil
+import sys
+from xml.dom import minidom
+
+kDevToolsResourcePrefix = 'IDR_DEVTOOLS_'
+kGrdTemplate = '''<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1">
+  <outputs>
+    <output filename="grit/devtools_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="grit/devtools_resources_map.cc" type="resource_file_map_source" />
+    <output filename="grit/devtools_resources_map.h" type="resource_map_header" />
+
+    <output filename="devtools_resources.pak" type="data_package" />
+  </outputs>
+  <release seq="1">
+    <includes></includes>
+  </release>
+</grit>
+'''
+
+
+class ParsedArgs:
+    def __init__(self, source_files, image_dirs, output_filename):
+        self.source_files = source_files
+        self.image_dirs = image_dirs
+        self.output_filename = output_filename
+
+
+def parse_args(argv):
+    images_position = argv.index('--images')
+    output_position = argv.index('--output')
+    source_files = argv[:images_position]
+    image_dirs = argv[images_position + 1:output_position]
+    return ParsedArgs(source_files, image_dirs, argv[output_position + 1])
+
+
+def make_name_from_filename(filename):
+    return (filename.replace('/', '_')
+                    .replace('\\', '_')
+                    .replace('.', '_')).upper()
+
+
+def add_file_to_grd(grd_doc, filename):
+    includes_node = grd_doc.getElementsByTagName('includes')[0]
+    includes_node.appendChild(grd_doc.createTextNode('\n      '))
+
+    new_include_node = grd_doc.createElement('include')
+    new_include_node.setAttribute('name', make_name_from_filename(filename))
+    new_include_node.setAttribute('file', filename)
+    new_include_node.setAttribute('type', 'BINDATA')
+    includes_node.appendChild(new_include_node)
+
+
+def main(argv):
+    parsed_args = parse_args(argv[1:])
+
+    doc = minidom.parseString(kGrdTemplate)
+    output_directory = os.path.dirname(parsed_args.output_filename)
+
+    try:
+        os.makedirs(os.path.join(output_directory, 'Images'))
+    except OSError, e:
+        if e.errno != errno.EEXIST:
+            raise e
+
+    for filename in parsed_args.source_files:
+        shutil.copy(filename, output_directory)
+        add_file_to_grd(doc, os.path.basename(filename))
+
+    for dirname in parsed_args.image_dirs:
+        for filename in os.listdir(dirname):
+            if not filename.endswith('.png') and not filename.endswith('.gif'):
+                continue
+            shutil.copy(os.path.join(dirname, filename),
+                        os.path.join(output_directory, 'Images'))
+            add_file_to_grd(doc, os.path.join('Images', filename))
+
+    with open(parsed_args.output_filename, 'w') as output_file:
+        output_file.write(doc.toxml(encoding='UTF-8'))
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
diff --git a/Source/devtools/scripts/generate_devtools_html.py b/Source/devtools/scripts/generate_devtools_html.py
new file mode 100644
index 0000000..b8e15c2
--- /dev/null
+++ b/Source/devtools/scripts/generate_devtools_html.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2010 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:
+#
+#         * 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.
+#
+
+import os.path
+import sys
+
+
+def generate_include_tag(resource_path):
+    (dir_name, file_name) = os.path.split(resource_path)
+    if (file_name.endswith('.js')):
+        return '    <script type="text/javascript" src="%s"></script>\n' % file_name
+    elif (file_name.endswith('.css')):
+        return '    <link rel="stylesheet" type="text/css" href="%s">\n' % file_name
+    else:
+        assert resource_path
+
+
+def write_devtools_html(inspector_file, devtools_file, debug):
+    for line in inspector_file:
+        if not debug and '<script ' in line:
+            continue
+        if not debug and '<link ' in line:
+            continue
+        if '</head>' in line and not debug:
+            devtools_file.write(generate_include_tag("inspector.css"))
+            devtools_file.write(generate_include_tag("inspector.js"))
+        devtools_file.write(line)
+        if '<head>' in line:
+            devtools_file.write(generate_include_tag("buildSystemOnly.js"))
+
+
+def main(argv):
+
+    if len(argv) < 4:
+        print('usage: %s inspector_html devtools_html debug' % argv[0])
+        return 1
+
+    # The first argument is ignored. We put 'webkit.gyp' in the inputs list
+    # for this script, so every time the list of script gets changed, our html
+    # file is rebuilt.
+    inspector_html_name = argv[1]
+    devtools_html_name = argv[2]
+    debug = argv[3] != '0'
+    inspector_html = open(inspector_html_name, 'r')
+    devtools_html = open(devtools_html_name, 'w')
+
+    write_devtools_html(inspector_html, devtools_html, debug)
+
+    devtools_html.close()
+    inspector_html.close()
+
+    # Touch output file directory to make sure that Xcode will copy
+    # modified resource files.
+    if sys.platform == 'darwin':
+        output_dir_name = os.path.dirname(devtools_html_name)
+        os.utime(output_dir_name, None)
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
diff --git a/Source/devtools/scripts/generate_protocol_externs.py b/Source/devtools/scripts/generate_protocol_externs.py
new file mode 100755
index 0000000..beeaf54
--- /dev/null
+++ b/Source/devtools/scripts/generate_protocol_externs.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python
+# 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:
+#
+#     * 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.
+
+import re
+
+type_traits = {
+    "any": "*",
+    "string": "string",
+    "integer": "number",
+    "number": "number",
+    "boolean": "boolean",
+    "array": "Array.<*>",
+    "object": "Object",
+}
+
+ref_types = {}
+
+
+def full_qualified_type_id(domain_name, type_id):
+    if type_id.find(".") == -1:
+        return "%s.%s" % (domain_name, type_id)
+    return type_id
+
+
+def fix_camel_case(name):
+    refined = re.sub(r'-(\w)', lambda pat: pat.group(1).upper(), name)
+    refined = to_title_case(refined)
+    return re.sub(r'(?i)HTML|XML|WML|API', lambda pat: pat.group(0).upper(), refined)
+
+
+def to_title_case(name):
+    return name[:1].upper() + name[1:]
+
+
+def generate_enum(name, json):
+    enum_members = []
+    for member in json["enum"]:
+        enum_members.append("    %s: \"%s\"" % (fix_camel_case(member), member))
+    return "\n/** @enum {string} */\n%s = {\n%s\n};\n" % (name, (",\n".join(enum_members)))
+
+
+def param_type(domain_name, param):
+    if "type" in param:
+        if param["type"] == "array":
+            items = param["items"]
+            return "Array.<%s>" % param_type(domain_name, items)
+        else:
+            return type_traits[param["type"]]
+    if "$ref" in param:
+        type_id = full_qualified_type_id(domain_name, param["$ref"])
+        if type_id in ref_types:
+            return ref_types[type_id]
+        else:
+            print "Type not found: " + type_id
+            return "!! Type not found: " + type_id
+
+
+def generate_protocol_externs(output_path, input_path):
+    input_file = open(input_path, "r")
+    json_string = input_file.read()
+    json_string = json_string.replace(": true", ": True")
+    json_string = json_string.replace(": false", ": False")
+    json_api = eval(json_string)["domains"]
+
+    output_file = open(output_path, "w")
+
+    output_file.write(
+"""
+var Protocol = {};
+/** @typedef {string}*/
+Protocol.Error;
+""")
+
+    for domain in json_api:
+        domain_name = domain["domain"]
+        if "types" in domain:
+            for type in domain["types"]:
+                type_id = full_qualified_type_id(domain_name, type["id"])
+                ref_types[type_id] = "%sAgent.%s" % (domain_name, type["id"])
+
+    for domain in json_api:
+        domain_name = domain["domain"]
+        output_file.write("\n\n\nvar %sAgent = {};\n" % domain_name)
+        if "types" in domain:
+            for type in domain["types"]:
+                if type["type"] == "object":
+                    typedef_args = []
+                    if "properties" in type:
+                        for property in type["properties"]:
+                            suffix = ""
+                            if ("optional" in property):
+                                suffix = "|undefined"
+                            if "enum" in property:
+                                enum_name = "%sAgent.%s%s" % (domain_name, type["id"], to_title_case(property["name"]))
+                                output_file.write(generate_enum(enum_name, property))
+                                typedef_args.append("%s:(%s%s)" % (property["name"], enum_name, suffix))
+                            else:
+                                typedef_args.append("%s:(%s%s)" % (property["name"], param_type(domain_name, property), suffix))
+                    if (typedef_args):
+                        output_file.write("\n/** @typedef {{%s}|null} */\n%sAgent.%s;\n" % (", ".join(typedef_args), domain_name, type["id"]))
+                    else:
+                        output_file.write("\n/** @typedef {Object} */\n%sAgent.%s;\n" % (domain_name, type["id"]))
+                elif type["type"] == "string" and "enum" in type:
+                    output_file.write(generate_enum("%sAgent.%s" % (domain_name, type["id"]), type))
+                elif type["type"] == "array":
+                    suffix = ""
+                    if ("optional" in property):
+                        suffix = "|undefined"
+                    output_file.write("\n/** @typedef {Array.<%s>%s} */\n%sAgent.%s;\n" % (param_type(domain_name, type["items"]), suffix, domain_name, type["id"]))
+                else:
+                    output_file.write("\n/** @typedef {%s} */\n%sAgent.%s;\n" % (type_traits[type["type"]], domain_name, type["id"]))
+
+        if "commands" in domain:
+            for command in domain["commands"]:
+                output_file.write("\n/**\n")
+                params = []
+                if ("parameters" in command):
+                    for in_param in command["parameters"]:
+                        if ("optional" in in_param):
+                            params.append("opt_%s" % in_param["name"])
+                            output_file.write(" * @param {%s=} opt_%s\n" % (param_type(domain_name, in_param), in_param["name"]))
+                        else:
+                            params.append(in_param["name"])
+                            output_file.write(" * @param {%s} %s\n" % (param_type(domain_name, in_param), in_param["name"]))
+                returns = ["?Protocol.Error"]
+                if ("returns" in command):
+                    for out_param in command["returns"]:
+                        if ("optional" in out_param):
+                            returns.append("%s=" % param_type(domain_name, out_param))
+                        else:
+                            returns.append("%s" % param_type(domain_name, out_param))
+                output_file.write(" * @param {function(%s):void=} opt_callback\n" % ", ".join(returns))
+                output_file.write(" */\n")
+                params.append("opt_callback")
+                output_file.write("%sAgent.%s = function(%s) {}\n" % (domain_name, command["name"], ", ".join(params)))
+                output_file.write("/** @param {function(%s):void=} opt_callback */\n" % ", ".join(returns))
+                output_file.write("%sAgent.%s.invoke = function(obj, opt_callback) {}\n" % (domain_name, command["name"]))
+
+        output_file.write("/** @interface */\n")
+        output_file.write("%sAgent.Dispatcher = function() {};\n" % domain_name)
+        if "events" in domain:
+            for event in domain["events"]:
+                params = []
+                if ("parameters" in event):
+                    output_file.write("/**\n")
+                    for param in event["parameters"]:
+                        if ("optional" in param):
+                            params.append("opt_%s" % param["name"])
+                            output_file.write(" * @param {%s=} opt_%s\n" % (param_type(domain_name, param), param["name"]))
+                        else:
+                            params.append(param["name"])
+                            output_file.write(" * @param {%s} %s\n" % (param_type(domain_name, param), param["name"]))
+                    output_file.write(" */\n")
+                output_file.write("%sAgent.Dispatcher.prototype.%s = function(%s) {};\n" % (domain_name, event["name"], ", ".join(params)))
+        output_file.write("/**\n * @param {%sAgent.Dispatcher} dispatcher\n */\n" % domain_name)
+        output_file.write("InspectorBackend.register%sDispatcher = function(dispatcher) {}\n" % domain_name)
+    output_file.close()
+
+if __name__ == "__main__":
+    import sys
+    import os.path
+    program_name = os.path.basename(__file__)
+    if len(sys.argv) < 4 or sys.argv[1] != "-o":
+        sys.stderr.write("Usage: %s -o OUTPUT_FILE INPUT_FILE\n" % program_name)
+        exit(1)
+    output_path = sys.argv[2]
+    input_path = sys.argv[3]
+    generate_protocol_externs(output_path, input_path)
diff --git a/Source/devtools/scripts/inline_js_imports.py b/Source/devtools/scripts/inline_js_imports.py
new file mode 100644
index 0000000..b65c350
--- /dev/null
+++ b/Source/devtools/scripts/inline_js_imports.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+#
+# 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:
+#
+#         * 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.
+#
+
+# This script replaces calls to importScripts with script sources
+# in input script file and dumps result into output script file.
+
+from cStringIO import StringIO
+
+import jsmin
+import os.path
+import re
+import sys
+
+
+def main(argv):
+
+    if len(argv) < 3:
+        print('usage: %s input_file imports_dir output_file no_minify' % argv[0])
+        return 1
+
+    input_file_name = argv[1]
+    imports_dir = argv[2]
+    output_file_name = argv[3]
+    no_minify = len(argv) > 4 and argv[4]
+
+    input_file = open(input_file_name, 'r')
+    input_script = input_file.read()
+    input_file.close()
+
+    def replace(match):
+        import_file_name = match.group(1)
+        full_path = os.path.join(imports_dir, import_file_name)
+        if not os.access(full_path, os.F_OK):
+            raise Exception('File %s referenced in %s not found on any source paths, '
+                            'check source tree for consistency' %
+                            (import_file_name, input_file_name))
+        import_file = open(full_path, 'r')
+        import_script = import_file.read()
+        import_file.close()
+        return import_script
+
+    output_script = re.sub(r'importScripts?\([\'"]([^\'"]+)[\'"]\)', replace, input_script)
+    if re.search("importScripts?\(\"", output_script):
+        raise Exception('Unresolved "importScript" statements found in "%s". '
+                        'Make sure you call "importScript" in module heads only.' %
+                        (output_file_name))
+
+    if os.path.exists(output_file_name):
+        os.remove(output_file_name)
+    output_file = open(output_file_name, 'w')
+    if not no_minify:
+        output_script = jsmin.jsmin(output_script)
+    output_file.write(output_script)
+    output_file.close()
+
+    # Touch output file directory to make sure that Xcode will copy
+    # modified resource files.
+    if sys.platform == 'darwin':
+        output_dir_name = os.path.dirname(output_file_name)
+        os.utime(output_dir_name, None)
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
diff --git a/Source/devtools/scripts/jsmin.py b/Source/devtools/scripts/jsmin.py
new file mode 100644
index 0000000..c2583a4
--- /dev/null
+++ b/Source/devtools/scripts/jsmin.py
@@ -0,0 +1,229 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2010 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:
+#
+#         * 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.
+#
+# This is a Python port of Douglas Crockford's jsmin.cc. See original
+# copyright notice below.
+#
+# Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is furnished to do
+# so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+
+from cStringIO import StringIO
+import sys
+
+
+class UnterminatedComment(Exception):
+    pass
+
+
+class UnterminatedStringLiteral(Exception):
+    pass
+
+
+class UnterminatedRegularExpression(Exception):
+    pass
+
+
+EOF = ''
+
+
+def jsmin(text):
+    minifier = JavaScriptMinifier()
+    minifier.input = StringIO(text)
+    minifier.output = StringIO()
+    minifier.jsmin()
+    return minifier.output.getvalue()
+
+
+class JavaScriptMinifier(object):
+
+    def isAlphanum(self, c):
+        """ return true if the character is a letter, digit, underscore,
+            dollar sign, or non-ASCII character.
+        """
+        return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
+            (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or
+            c > 126)
+
+    def get(self):
+        """ return the next character from stdin. Watch out for lookahead. If
+            the character is a control character, translate it to a space or
+            linefeed.
+        """
+        c = self.theLookahead
+        self.theLookahead = EOF
+        if c == EOF:
+            c = self.input.read(1)
+        if c >= ' ' or c == '\n' or c == EOF:
+            return c
+        if c == '\r':
+            return '\n'
+        return ' '
+
+    def peek(self):
+        """ get the next character without getting it. """
+        self.theLookahead = self.get()
+        return self.theLookahead
+
+    def next(self):
+        """ get the next character, excluding comments. peek() is used to see
+            if a '/' is followed by a '/' or '*'.
+        """
+        c = self.get()
+        if c == '/':
+            peek = self.peek()
+            if peek == '/':
+                while True:
+                    c = self.get()
+                    if c <= '\n':
+                        return c
+            elif peek == '*':
+                self.get()
+                while True:
+                    get = self.get()
+                    if get == '*':
+                        if self.peek() == '/':
+                            self.get()
+                            return ' '
+                    elif get == EOF:
+                        raise UnterminatedComment()
+            else:
+                return c
+        return c
+
+    def putc(self, c):
+        self.output.write(c)
+
+    def action(self, d):
+        """ do something! What you do is determined by the argument:
+               1   Output A. Copy B to A. Get the next B.
+               2   Copy B to A. Get the next B. (Delete A).
+               3   Get the next B. (Delete B).
+            action treats a string as a single character. Wow!
+            action recognizes a regular expression if it is preceded by ( or , or =.
+        """
+        if d <= 1:
+            self.putc(self.theA)
+        if d <= 2:
+            self.theA = self.theB
+            if self.theA == '\'' or self.theA == '"':
+                while True:
+                    self.putc(self.theA)
+                    self.theA = self.get()
+                    if self.theA == self.theB:
+                        break
+                    if self.theA == '\\':
+                        self.putc(self.theA)
+                        self.theA = self.get()
+                    if self.theA == EOF:
+                        raise UnterminatedString()
+        if d <= 3:
+            self.theB = self.next()
+            if self.theB == '/' and self.theA in ['(', ',', '=', ':', '[', '!', '&', '|', '?', '{', '}', ';', '\n']:
+                self.putc(self.theA)
+                self.putc(self.theB)
+                while True:
+                    self.theA = self.get()
+                    if self.theA == '/':
+                        break
+                    if self.theA == '\\':
+                        self.putc(self.theA)
+                        self.theA = self.get()
+                    if self.theA == EOF:
+                        raise UnterminatedRegularExpression()
+                    self.putc(self.theA)
+                self.theB = self.next()
+
+    def jsmin(self):
+        """ Copy the input to the output, deleting the characters which are
+            insignificant to JavaScript. Comments will be removed. Tabs will be
+            replaced with spaces. Carriage returns will be replaced with linefeeds.
+            Most spaces and linefeeds will be removed.
+        """
+        self.theA = '\n'
+        self.theLookahead = EOF
+        self.action(3)
+        while self.theA != EOF:
+            if self.theA == ' ':
+                if self.isAlphanum(self.theB):
+                    self.action(1)
+                else:
+                    self.action(2)
+            elif self.theA == '\n':
+                if self.theB in ['{', '[', '(', '+', '-']:
+                    self.action(1)
+                elif self.theB == ' ':
+                    self.action(3)
+                else:
+                    if self.isAlphanum(self.theB):
+                        self.action(1)
+                    else:
+                        self.action(2)
+            else:
+                if self.theB == ' ':
+                    if self.isAlphanum(self.theA):
+                        self.action(1)
+                    else:
+                        self.action(3)
+                elif self.theB == '\n':
+                    if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
+                        self.action(1)
+                    else:
+                        if self.isAlphanum(self.theA):
+                            self.action(1)
+                        else:
+                            self.action(3)
+                else:
+                    self.action(1)
+
+
+if __name__ == '__main__':
+    minifier = JavaScriptMinifier()
+    minifier.input = sys.stdin
+    minifier.output = sys.stdout
+    minifier.jsmin()
+    sys.stdin.close()