summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/Models/Resource.js
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Models/Resource.js
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Models/Resource.js')
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/Resource.js804
1 files changed, 804 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Models/Resource.js b/Source/WebInspectorUI/UserInterface/Models/Resource.js
new file mode 100644
index 000000000..29e5a7d6a
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/Resource.js
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Resource = class Resource extends WebInspector.SourceCode
+{
+ constructor(url, mimeType, type, loaderIdentifier, targetId, requestIdentifier, requestMethod, requestHeaders, requestData, requestSentTimestamp, initiatorSourceCodeLocation, originalRequestWillBeSentTimestamp)
+ {
+ super();
+
+ console.assert(url);
+
+ if (type in WebInspector.Resource.Type)
+ type = WebInspector.Resource.Type[type];
+
+ this._url = url;
+ this._urlComponents = null;
+ this._mimeType = mimeType;
+ this._mimeTypeComponents = null;
+ this._type = type || WebInspector.Resource.typeFromMIMEType(mimeType);
+ this._loaderIdentifier = loaderIdentifier || null;
+ this._requestIdentifier = requestIdentifier || null;
+ this._requestMethod = requestMethod || null;
+ this._requestData = requestData || null;
+ this._requestHeaders = requestHeaders || {};
+ this._responseHeaders = {};
+ this._parentFrame = null;
+ this._initiatorSourceCodeLocation = initiatorSourceCodeLocation || null;
+ this._initiatedResources = [];
+ this._originalRequestWillBeSentTimestamp = originalRequestWillBeSentTimestamp || null;
+ this._requestSentTimestamp = requestSentTimestamp || NaN;
+ this._responseReceivedTimestamp = NaN;
+ this._lastRedirectReceivedTimestamp = NaN;
+ this._lastDataReceivedTimestamp = NaN;
+ this._finishedOrFailedTimestamp = NaN;
+ this._finishThenRequestContentPromise = null;
+ this._size = NaN;
+ this._transferSize = NaN;
+ this._cached = false;
+ this._timingData = new WebInspector.ResourceTimingData(this);
+ this._target = targetId ? WebInspector.targetManager.targetForIdentifier(targetId) : WebInspector.mainTarget;
+
+ if (this._initiatorSourceCodeLocation && this._initiatorSourceCodeLocation.sourceCode instanceof WebInspector.Resource)
+ this._initiatorSourceCodeLocation.sourceCode.addInitiatedResource(this);
+ }
+
+ // Static
+
+ static typeFromMIMEType(mimeType)
+ {
+ if (!mimeType)
+ return WebInspector.Resource.Type.Other;
+
+ mimeType = parseMIMEType(mimeType).type;
+
+ if (mimeType in WebInspector.Resource._mimeTypeMap)
+ return WebInspector.Resource._mimeTypeMap[mimeType];
+
+ if (mimeType.startsWith("image/"))
+ return WebInspector.Resource.Type.Image;
+
+ if (mimeType.startsWith("font/"))
+ return WebInspector.Resource.Type.Font;
+
+ return WebInspector.Resource.Type.Other;
+ }
+
+ static displayNameForType(type, plural)
+ {
+ switch (type) {
+ case WebInspector.Resource.Type.Document:
+ if (plural)
+ return WebInspector.UIString("Documents");
+ return WebInspector.UIString("Document");
+ case WebInspector.Resource.Type.Stylesheet:
+ if (plural)
+ return WebInspector.UIString("Stylesheets");
+ return WebInspector.UIString("Stylesheet");
+ case WebInspector.Resource.Type.Image:
+ if (plural)
+ return WebInspector.UIString("Images");
+ return WebInspector.UIString("Image");
+ case WebInspector.Resource.Type.Font:
+ if (plural)
+ return WebInspector.UIString("Fonts");
+ return WebInspector.UIString("Font");
+ case WebInspector.Resource.Type.Script:
+ if (plural)
+ return WebInspector.UIString("Scripts");
+ return WebInspector.UIString("Script");
+ case WebInspector.Resource.Type.XHR:
+ if (plural)
+ return WebInspector.UIString("XHRs");
+ return WebInspector.UIString("XHR");
+ case WebInspector.Resource.Type.Fetch:
+ if (plural)
+ return WebInspector.UIString("Fetches");
+ return WebInspector.UIString("Fetch");
+ case WebInspector.Resource.Type.WebSocket:
+ if (plural)
+ return WebInspector.UIString("Sockets");
+ return WebInspector.UIString("Socket");
+ case WebInspector.Resource.Type.Other:
+ return WebInspector.UIString("Other");
+ default:
+ console.error("Unknown resource type: ", type);
+ return null;
+ }
+ }
+
+ // Public
+
+ get target() { return this._target; }
+ get type() { return this._type; }
+ get timingData() { return this._timingData; }
+
+ get url()
+ {
+ return this._url;
+ }
+
+ get urlComponents()
+ {
+ if (!this._urlComponents)
+ this._urlComponents = parseURL(this._url);
+ return this._urlComponents;
+ }
+
+ get displayName()
+ {
+ return WebInspector.displayNameForURL(this._url, this.urlComponents);
+ }
+
+ get displayURL()
+ {
+ const isMultiLine = true;
+ const dataURIMaxSize = 64;
+ return WebInspector.truncateURL(this._url, isMultiLine, dataURIMaxSize);
+ }
+
+ get initiatorSourceCodeLocation()
+ {
+ return this._initiatorSourceCodeLocation;
+ }
+
+ get initiatedResources()
+ {
+ return this._initiatedResources;
+ }
+
+ get originalRequestWillBeSentTimestamp()
+ {
+ return this._originalRequestWillBeSentTimestamp;
+ }
+
+ get mimeType()
+ {
+ return this._mimeType;
+ }
+
+ get mimeTypeComponents()
+ {
+ if (!this._mimeTypeComponents)
+ this._mimeTypeComponents = parseMIMEType(this._mimeType);
+ return this._mimeTypeComponents;
+ }
+
+ get syntheticMIMEType()
+ {
+ // Resources are often transferred with a MIME-type that doesn't match the purpose the
+ // resource was loaded for, which is what WebInspector.Resource.Type represents.
+ // This getter generates a MIME-type, if needed, that matches the resource type.
+
+ // If the type matches the Resource.Type of the MIME-type, then return the actual MIME-type.
+ if (this._type === WebInspector.Resource.typeFromMIMEType(this._mimeType))
+ return this._mimeType;
+
+ // Return the default MIME-types for the Resource.Type, since the current MIME-type
+ // does not match what is expected for the Resource.Type.
+ switch (this._type) {
+ case WebInspector.Resource.Type.Document:
+ return "text/html";
+ case WebInspector.Resource.Type.Stylesheet:
+ return "text/css";
+ case WebInspector.Resource.Type.Script:
+ return "text/javascript";
+ }
+
+ // Return the actual MIME-type since we don't have a better synthesized one to return.
+ return this._mimeType;
+ }
+
+ createObjectURL()
+ {
+ // If content is not available, fallback to using original URL.
+ // The client may try to revoke it, but nothing will happen.
+ if (!this.content)
+ return this._url;
+
+ var content = this.content;
+ console.assert(content instanceof Blob, content);
+
+ return URL.createObjectURL(content);
+ }
+
+ isMainResource()
+ {
+ return this._parentFrame ? this._parentFrame.mainResource === this : false;
+ }
+
+ addInitiatedResource(resource)
+ {
+ if (!(resource instanceof WebInspector.Resource))
+ return;
+
+ this._initiatedResources.push(resource);
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.InitiatedResourcesDidChange);
+ }
+
+ get parentFrame()
+ {
+ return this._parentFrame;
+ }
+
+ get loaderIdentifier()
+ {
+ return this._loaderIdentifier;
+ }
+
+ get requestIdentifier()
+ {
+ return this._requestIdentifier;
+ }
+
+ get finished()
+ {
+ return this._finished;
+ }
+
+ get failed()
+ {
+ return this._failed;
+ }
+
+ get canceled()
+ {
+ return this._canceled;
+ }
+
+ get requestMethod()
+ {
+ return this._requestMethod;
+ }
+
+ get requestData()
+ {
+ return this._requestData;
+ }
+
+ get requestDataContentType()
+ {
+ return this._requestHeaders.valueForCaseInsensitiveKey("Content-Type") || null;
+ }
+
+ get requestHeaders()
+ {
+ return this._requestHeaders;
+ }
+
+ get responseHeaders()
+ {
+ return this._responseHeaders;
+ }
+
+ get requestSentTimestamp()
+ {
+ return this._requestSentTimestamp;
+ }
+
+ get lastRedirectReceivedTimestamp()
+ {
+ return this._lastRedirectReceivedTimestamp;
+ }
+
+ get responseReceivedTimestamp()
+ {
+ return this._responseReceivedTimestamp;
+ }
+
+ get lastDataReceivedTimestamp()
+ {
+ return this._lastDataReceivedTimestamp;
+ }
+
+ get finishedOrFailedTimestamp()
+ {
+ return this._finishedOrFailedTimestamp;
+ }
+
+ get firstTimestamp()
+ {
+ return this.timingData.startTime || this.lastRedirectReceivedTimestamp || this.responseReceivedTimestamp || this.lastDataReceivedTimestamp || this.finishedOrFailedTimestamp;
+ }
+
+ get lastTimestamp()
+ {
+ return this.timingData.responseEnd || this.lastDataReceivedTimestamp || this.responseReceivedTimestamp || this.lastRedirectReceivedTimestamp || this.requestSentTimestamp;
+ }
+
+ get duration()
+ {
+ return this.timingData.responseEnd - this.timingData.requestStart;
+ }
+
+ get latency()
+ {
+ return this.timingData.responseStart - this.timingData.requestStart;
+ }
+
+ get receiveDuration()
+ {
+ return this.timingData.responseEnd - this.timingData.responseStart;
+ }
+
+ get cached()
+ {
+ return this._cached;
+ }
+
+ get statusCode()
+ {
+ return this._statusCode;
+ }
+
+ get statusText()
+ {
+ return this._statusText;
+ }
+
+ get size()
+ {
+ return this._size;
+ }
+
+ get encodedSize()
+ {
+ if (!isNaN(this._transferSize))
+ return this._transferSize;
+
+ // If we did not receive actual transfer size from network
+ // stack, we prefer using Content-Length over resourceSize as
+ // resourceSize may differ from actual transfer size if platform's
+ // network stack performed decoding (e.g. gzip decompression).
+ // The Content-Length, though, is expected to come from raw
+ // response headers and will reflect actual transfer length.
+ // This won't work for chunked content encoding, so fall back to
+ // resourceSize when we don't have Content-Length. This still won't
+ // work for chunks with non-trivial encodings. We need a way to
+ // get actual transfer size from the network stack.
+
+ return Number(this._responseHeaders.valueForCaseInsensitiveKey("Content-Length") || this._size);
+ }
+
+ get transferSize()
+ {
+ if (this.statusCode === 304) // Not modified
+ return this._responseHeadersSize;
+
+ if (this._cached)
+ return 0;
+
+ return this._responseHeadersSize + this.encodedSize;
+ }
+
+ get compressed()
+ {
+ var contentEncoding = this._responseHeaders.valueForCaseInsensitiveKey("Content-Encoding");
+ return contentEncoding && /\b(?:gzip|deflate)\b/.test(contentEncoding);
+ }
+
+ get scripts()
+ {
+ return this._scripts || [];
+ }
+
+ scriptForLocation(sourceCodeLocation)
+ {
+ console.assert(!(this instanceof WebInspector.SourceMapResource));
+ console.assert(sourceCodeLocation.sourceCode === this, "SourceCodeLocation must be in this Resource");
+ if (sourceCodeLocation.sourceCode !== this)
+ return null;
+
+ var lineNumber = sourceCodeLocation.lineNumber;
+ var columnNumber = sourceCodeLocation.columnNumber;
+ for (var i = 0; i < this._scripts.length; ++i) {
+ var script = this._scripts[i];
+ if (script.range.startLine <= lineNumber && script.range.endLine >= lineNumber) {
+ if (script.range.startLine === lineNumber && columnNumber < script.range.startColumn)
+ continue;
+ if (script.range.endLine === lineNumber && columnNumber > script.range.endColumn)
+ continue;
+ return script;
+ }
+ }
+
+ return null;
+ }
+
+ updateForRedirectResponse(url, requestHeaders, elapsedTime)
+ {
+ console.assert(!this._finished);
+ console.assert(!this._failed);
+ console.assert(!this._canceled);
+
+ var oldURL = this._url;
+
+ this._url = url;
+ this._requestHeaders = requestHeaders || {};
+ this._lastRedirectReceivedTimestamp = elapsedTime || NaN;
+
+ if (oldURL !== url) {
+ // Delete the URL components so the URL is re-parsed the next time it is requested.
+ this._urlComponents = null;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.URLDidChange, {oldURL});
+ }
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.RequestHeadersDidChange);
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
+ }
+
+ updateForResponse(url, mimeType, type, responseHeaders, statusCode, statusText, elapsedTime, timingData)
+ {
+ console.assert(!this._finished);
+ console.assert(!this._failed);
+ console.assert(!this._canceled);
+
+ var oldURL = this._url;
+ var oldMIMEType = this._mimeType;
+ var oldType = this._type;
+
+ if (type in WebInspector.Resource.Type)
+ type = WebInspector.Resource.Type[type];
+
+ this._url = url;
+ this._mimeType = mimeType;
+ this._type = type || WebInspector.Resource.typeFromMIMEType(mimeType);
+ this._statusCode = statusCode;
+ this._statusText = statusText;
+ this._responseHeaders = responseHeaders || {};
+ this._responseReceivedTimestamp = elapsedTime || NaN;
+ this._timingData = WebInspector.ResourceTimingData.fromPayload(timingData, this);
+
+ this._responseHeadersSize = String(this._statusCode).length + this._statusText.length + 12; // Extra length is for "HTTP/1.1 ", " ", and "\r\n".
+ for (var name in this._responseHeaders)
+ this._responseHeadersSize += name.length + this._responseHeaders[name].length + 4; // Extra length is for ": ", and "\r\n".
+
+ if (statusCode === 304 && !this._cached)
+ this.markAsCached();
+
+ if (oldURL !== url) {
+ // Delete the URL components so the URL is re-parsed the next time it is requested.
+ this._urlComponents = null;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.URLDidChange, {oldURL});
+ }
+
+ if (oldMIMEType !== mimeType) {
+ // Delete the MIME-type components so the MIME-type is re-parsed the next time it is requested.
+ this._mimeTypeComponents = null;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.MIMETypeDidChange, {oldMIMEType});
+ }
+
+ if (oldType !== type)
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TypeDidChange, {oldType});
+
+ console.assert(isNaN(this._size));
+ console.assert(isNaN(this._transferSize));
+
+ // The transferSize becomes 0 when status is 304 or Content-Length is available, so
+ // notify listeners of that change.
+ if (statusCode === 304 || this._responseHeaders.valueForCaseInsensitiveKey("Content-Length"))
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.ResponseReceived);
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
+ }
+
+ canRequestContent()
+ {
+ return this._finished;
+ }
+
+ requestContentFromBackend()
+ {
+ // If we have the requestIdentifier we can get the actual response for this specific resource.
+ // Otherwise the content will be cached resource data, which might not exist anymore.
+ if (this._requestIdentifier)
+ return NetworkAgent.getResponseBody(this._requestIdentifier);
+
+ // There is no request identifier or frame to request content from.
+ if (this._parentFrame)
+ return PageAgent.getResourceContent(this._parentFrame.id, this._url);
+
+ return Promise.reject(new Error("Content request failed."));
+ }
+
+ increaseSize(dataLength, elapsedTime)
+ {
+ console.assert(dataLength >= 0);
+
+ if (isNaN(this._size))
+ this._size = 0;
+
+ var previousSize = this._size;
+
+ this._size += dataLength;
+
+ this._lastDataReceivedTimestamp = elapsedTime || NaN;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.SizeDidChange, {previousSize});
+
+ // The transferSize is based off of size when status is not 304 or Content-Length is missing.
+ if (isNaN(this._transferSize) && this._statusCode !== 304 && !this._responseHeaders.valueForCaseInsensitiveKey("Content-Length"))
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
+ }
+
+ increaseTransferSize(encodedDataLength)
+ {
+ console.assert(encodedDataLength >= 0);
+
+ if (isNaN(this._transferSize))
+ this._transferSize = 0;
+ this._transferSize += encodedDataLength;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
+ }
+
+ markAsCached()
+ {
+ this._cached = true;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.CacheStatusDidChange);
+
+ // The transferSize is starts returning 0 when cached is true, unless status is 304.
+ if (this._statusCode !== 304)
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TransferSizeDidChange);
+ }
+
+ markAsFinished(elapsedTime)
+ {
+ console.assert(!this._failed);
+ console.assert(!this._canceled);
+
+ this._finished = true;
+ this._finishedOrFailedTimestamp = elapsedTime || NaN;
+ this._timingData.markResponseEndTime(elapsedTime || NaN);
+
+ if (this._finishThenRequestContentPromise)
+ this._finishThenRequestContentPromise = null;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.LoadingDidFinish);
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
+ }
+
+ markAsFailed(canceled, elapsedTime)
+ {
+ console.assert(!this._finished);
+
+ this._failed = true;
+ this._canceled = canceled;
+ this._finishedOrFailedTimestamp = elapsedTime || NaN;
+
+ this.dispatchEventToListeners(WebInspector.Resource.Event.LoadingDidFail);
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TimestampsDidChange);
+ }
+
+ revertMarkAsFinished()
+ {
+ console.assert(!this._failed);
+ console.assert(!this._canceled);
+ console.assert(this._finished);
+
+ this._finished = false;
+ this._finishedOrFailedTimestamp = NaN;
+ }
+
+ getImageSize(callback)
+ {
+ // Throw an error in the case this resource is not an image.
+ if (this.type !== WebInspector.Resource.Type.Image)
+ throw "Resource is not an image.";
+
+ // See if we've already computed and cached the image size,
+ // in which case we can provide them directly.
+ if (this._imageSize) {
+ callback(this._imageSize);
+ return;
+ }
+
+ var objectURL = null;
+
+ // Event handler for the image "load" event.
+ function imageDidLoad()
+ {
+ URL.revokeObjectURL(objectURL);
+
+ // Cache the image metrics.
+ this._imageSize = {
+ width: image.width,
+ height: image.height
+ };
+
+ callback(this._imageSize);
+ }
+
+ // Create an <img> element that we'll use to load the image resource
+ // so that we can query its intrinsic size.
+ var image = new Image;
+ image.addEventListener("load", imageDidLoad.bind(this), false);
+
+ // Set the image source using an object URL once we've obtained its data.
+ this.requestContent().then(function(content) {
+ objectURL = image.src = content.sourceCode.createObjectURL();
+ });
+ }
+
+ requestContent()
+ {
+ if (this._finished)
+ return super.requestContent();
+
+ if (this._failed)
+ return Promise.resolve({error: WebInspector.UIString("An error occurred trying to load the resource.")});
+
+ if (!this._finishThenRequestContentPromise) {
+ this._finishThenRequestContentPromise = new Promise((resolve, reject) => {
+ this.addEventListener(WebInspector.Resource.Event.LoadingDidFinish, resolve);
+ this.addEventListener(WebInspector.Resource.Event.LoadingDidFail, reject);
+ }).then(WebInspector.SourceCode.prototype.requestContent.bind(this));
+ }
+
+ return this._finishThenRequestContentPromise;
+ }
+
+ associateWithScript(script)
+ {
+ if (!this._scripts)
+ this._scripts = [];
+
+ this._scripts.push(script);
+
+ if (this._type === WebInspector.Resource.Type.Other || this._type === WebInspector.Resource.Type.XHR) {
+ let oldType = this._type;
+ this._type = WebInspector.Resource.Type.Script;
+ this.dispatchEventToListeners(WebInspector.Resource.Event.TypeDidChange, {oldType});
+ }
+ }
+
+ saveIdentityToCookie(cookie)
+ {
+ cookie[WebInspector.Resource.URLCookieKey] = this.url.hash;
+ cookie[WebInspector.Resource.MainResourceCookieKey] = this.isMainResource();
+ }
+
+ generateCURLCommand()
+ {
+ function escapeStringPosix(str) {
+ function escapeCharacter(x) {
+ let code = x.charCodeAt(0);
+ let hex = code.toString(16);
+ if (code < 256)
+ return "\\x" + hex.padStart(2, "0");
+ return "\\u" + hex.padStart(4, "0");
+ }
+
+ if (/[^\x20-\x7E]|'/.test(str)) {
+ // Use ANSI-C quoting syntax.
+ return "$'" + str.replace(/\\/g, "\\\\")
+ .replace(/'/g, "\\'")
+ .replace(/\n/g, "\\n")
+ .replace(/\r/g, "\\r")
+ .replace(/[^\x20-\x7E]/g, escapeCharacter) + "'";
+ } else {
+ // Use single quote syntax.
+ return `'${str}'`;
+ }
+ }
+
+ let command = ["curl " + escapeStringPosix(this.url).replace(/[[{}\]]/g, "\\$&")];
+ command.push(`-X${this.requestMethod}`);
+
+ for (let key in this.requestHeaders)
+ command.push("-H " + escapeStringPosix(`${key}: ${this.requestHeaders[key]}`));
+
+ if (this.requestDataContentType && this.requestMethod !== "GET" && this.requestData) {
+ if (this.requestDataContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i))
+ command.push("--data " + escapeStringPosix(this.requestData));
+ else
+ command.push("--data-binary " + escapeStringPosix(this.requestData));
+ }
+
+ let curlCommand = command.join(" \\\n");
+ InspectorFrontendHost.copyText(curlCommand);
+ return curlCommand;
+ }
+};
+
+WebInspector.Resource.TypeIdentifier = "resource";
+WebInspector.Resource.URLCookieKey = "resource-url";
+WebInspector.Resource.MainResourceCookieKey = "resource-is-main-resource";
+
+WebInspector.Resource.Event = {
+ URLDidChange: "resource-url-did-change",
+ MIMETypeDidChange: "resource-mime-type-did-change",
+ TypeDidChange: "resource-type-did-change",
+ RequestHeadersDidChange: "resource-request-headers-did-change",
+ ResponseReceived: "resource-response-received",
+ LoadingDidFinish: "resource-loading-did-finish",
+ LoadingDidFail: "resource-loading-did-fail",
+ TimestampsDidChange: "resource-timestamps-did-change",
+ SizeDidChange: "resource-size-did-change",
+ TransferSizeDidChange: "resource-transfer-size-did-change",
+ CacheStatusDidChange: "resource-cached-did-change",
+ InitiatedResourcesDidChange: "resource-initiated-resources-did-change",
+};
+
+// Keep these in sync with the "ResourceType" enum defined by the "Page" domain.
+WebInspector.Resource.Type = {
+ Document: "resource-type-document",
+ Stylesheet: "resource-type-stylesheet",
+ Image: "resource-type-image",
+ Font: "resource-type-font",
+ Script: "resource-type-script",
+ XHR: "resource-type-xhr",
+ Fetch: "resource-type-fetch",
+ WebSocket: "resource-type-websocket",
+ Other: "resource-type-other"
+};
+
+// This MIME Type map is private, use WebInspector.Resource.typeFromMIMEType().
+WebInspector.Resource._mimeTypeMap = {
+ "text/html": WebInspector.Resource.Type.Document,
+ "text/xml": WebInspector.Resource.Type.Document,
+ "text/plain": WebInspector.Resource.Type.Document,
+ "application/xhtml+xml": WebInspector.Resource.Type.Document,
+ "image/svg+xml": WebInspector.Resource.Type.Document,
+
+ "text/css": WebInspector.Resource.Type.Stylesheet,
+ "text/xsl": WebInspector.Resource.Type.Stylesheet,
+ "text/x-less": WebInspector.Resource.Type.Stylesheet,
+ "text/x-sass": WebInspector.Resource.Type.Stylesheet,
+ "text/x-scss": WebInspector.Resource.Type.Stylesheet,
+
+ "application/pdf": WebInspector.Resource.Type.Image,
+
+ "application/x-font-type1": WebInspector.Resource.Type.Font,
+ "application/x-font-ttf": WebInspector.Resource.Type.Font,
+ "application/x-font-woff": WebInspector.Resource.Type.Font,
+ "application/x-truetype-font": WebInspector.Resource.Type.Font,
+
+ "text/javascript": WebInspector.Resource.Type.Script,
+ "text/ecmascript": WebInspector.Resource.Type.Script,
+ "application/javascript": WebInspector.Resource.Type.Script,
+ "application/ecmascript": WebInspector.Resource.Type.Script,
+ "application/x-javascript": WebInspector.Resource.Type.Script,
+ "application/json": WebInspector.Resource.Type.Script,
+ "application/x-json": WebInspector.Resource.Type.Script,
+ "text/x-javascript": WebInspector.Resource.Type.Script,
+ "text/x-json": WebInspector.Resource.Type.Script,
+ "text/javascript1.1": WebInspector.Resource.Type.Script,
+ "text/javascript1.2": WebInspector.Resource.Type.Script,
+ "text/javascript1.3": WebInspector.Resource.Type.Script,
+ "text/jscript": WebInspector.Resource.Type.Script,
+ "text/livescript": WebInspector.Resource.Type.Script,
+ "text/x-livescript": WebInspector.Resource.Type.Script,
+ "text/typescript": WebInspector.Resource.Type.Script,
+ "text/x-clojure": WebInspector.Resource.Type.Script,
+ "text/x-coffeescript": WebInspector.Resource.Type.Script
+};