| /* |
| * Copyright (C) 2013 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WI.SourceMapResource = class SourceMapResource extends WI.Resource |
| { |
| constructor(url, sourceMap) |
| { |
| super(url); |
| |
| console.assert(url); |
| console.assert(sourceMap); |
| |
| this._sourceMap = sourceMap; |
| |
| var inheritedMIMEType = this._sourceMap.originalSourceCode instanceof WI.Resource ? this._sourceMap.originalSourceCode.syntheticMIMEType : null; |
| |
| let fileExtension = WI.fileExtensionForURL(url) || ""; |
| |
| // React serves JSX resources with "js" extension. |
| let fileExtensionMIMEType = fileExtension === "js" ? "text/jsx" : WI.mimeTypeForFileExtension(fileExtension, true); |
| |
| // FIXME: This is a layering violation. It should use a helper function on the |
| // Resource base-class to set _mimeType and _type. |
| this._mimeType = fileExtensionMIMEType || inheritedMIMEType || "text/javascript"; |
| this._type = WI.Resource.typeFromMIMEType(this._mimeType); |
| |
| // Mark the resource as loaded so it does not show a spinner in the sidebar. |
| // We will really load the resource the first time content is requested. |
| this.markAsFinished(); |
| } |
| |
| // Public |
| |
| get sourceMap() { return this._sourceMap; } |
| |
| get sourceMapDisplaySubpath() |
| { |
| var sourceMappingBasePathURLComponents = this._sourceMap.sourceMappingBasePathURLComponents; |
| var resourceURLComponents = this.urlComponents; |
| |
| // Fallback for JavaScript debuggable named scripts that may not have a complete URL. |
| if (!resourceURLComponents.path) |
| resourceURLComponents.path = this.url; |
| |
| // Different schemes / hosts. Return the host + path of this resource. |
| if (resourceURLComponents.scheme !== sourceMappingBasePathURLComponents.scheme || resourceURLComponents.host !== sourceMappingBasePathURLComponents.host) { |
| let subpath = ""; |
| if (resourceURLComponents.host) { |
| subpath += resourceURLComponents.host; |
| if (resourceURLComponents.port) |
| subpath += ":" + resourceURLComponents.port; |
| subpath += resourceURLComponents.path; |
| } else { |
| // Remove the leading "/" so there isn't an empty folder. |
| subpath += resourceURLComponents.path.substring(1); |
| } |
| return subpath; |
| } |
| |
| // Same host, but not a subpath of the base. This implies a ".." in the relative path. |
| if (!resourceURLComponents.path.startsWith(sourceMappingBasePathURLComponents.path)) |
| return relativePath(resourceURLComponents.path, sourceMappingBasePathURLComponents.path); |
| |
| // Same host. Just a subpath of the base. |
| return resourceURLComponents.path.substring(sourceMappingBasePathURLComponents.path.length, resourceURLComponents.length); |
| } |
| |
| get supportsScriptBlackboxing() |
| { |
| return false; |
| } |
| |
| requestContentFromBackend() |
| { |
| // Revert the markAsFinished that was done in the constructor. |
| this.revertMarkAsFinished(); |
| |
| var inlineContent = this._sourceMap.sourceContent(this.url); |
| if (inlineContent) { |
| // Force inline content to be asynchronous to match the expected load pattern. |
| // FIXME: We don't know the MIME-type for inline content. Guess by analyzing the content? |
| // Returns a promise. |
| return Promise.resolve().then(sourceMapResourceLoaded.bind(this, {content: inlineContent, mimeType: this.mimeType, statusCode: 200})); |
| } |
| |
| function sourceMapResourceNotAvailable(error, content, mimeType, statusCode) |
| { |
| this.markAsFailed(); |
| return Promise.resolve({ |
| error: WI.UIString("An error occurred trying to load the resource."), |
| content, |
| mimeType, |
| statusCode |
| }); |
| } |
| |
| function sourceMapResourceLoadError(error) |
| { |
| console.error(error || "There was an unknown error calling Network.loadResource."); |
| this.markAsFailed(); |
| return Promise.resolve({error: WI.UIString("An error occurred trying to load the resource.")}); |
| } |
| |
| function sourceMapResourceLoaded(parameters) |
| { |
| var {error, content, mimeType, statusCode} = parameters; |
| |
| var base64encoded = false; |
| |
| if (statusCode >= 400 || error) |
| return sourceMapResourceNotAvailable(error, content, mimeType, statusCode); |
| |
| // FIXME: Add support for picking the best MIME-type. Right now the file extension is the best bet. |
| // The constructor set MIME-type based on the file extension and we ignore mimeType here. |
| |
| this.markAsFinished(); |
| |
| return Promise.resolve({ |
| content, |
| mimeType, |
| base64encoded, |
| statusCode |
| }); |
| } |
| |
| if (!this._target.hasCommand("Network.loadResource")) |
| return sourceMapResourceLoadError.call(this); |
| |
| var frameIdentifier = null; |
| if (this._sourceMap.originalSourceCode instanceof WI.Resource && this._sourceMap.originalSourceCode.parentFrame) |
| frameIdentifier = this._sourceMap.originalSourceCode.parentFrame.id; |
| |
| if (!frameIdentifier) |
| frameIdentifier = WI.networkManager.mainFrame ? WI.networkManager.mainFrame.id : ""; |
| |
| return this._target.NetworkAgent.loadResource(frameIdentifier, this.url).then(sourceMapResourceLoaded.bind(this)).catch(sourceMapResourceLoadError.bind(this)); |
| } |
| |
| createSourceCodeLocation(lineNumber, columnNumber) |
| { |
| // SourceCodeLocations are always constructed with raw resources and raw locations. Lookup the raw location. |
| var entry = this._sourceMap.findEntryReversed(this.url, lineNumber); |
| var rawLineNumber = entry[0]; |
| var rawColumnNumber = entry[1]; |
| |
| // If the raw location is an inline script we need to include that offset. |
| var originalSourceCode = this._sourceMap.originalSourceCode; |
| if (originalSourceCode instanceof WI.Script) { |
| if (rawLineNumber === 0) |
| rawColumnNumber += originalSourceCode.range.startColumn; |
| rawLineNumber += originalSourceCode.range.startLine; |
| } |
| |
| // Create the SourceCodeLocation and since we already know the the mapped location set it directly. |
| var location = originalSourceCode.createSourceCodeLocation(rawLineNumber, rawColumnNumber); |
| location._setMappedLocation(this, lineNumber, columnNumber); |
| return location; |
| } |
| |
| createSourceCodeTextRange(textRange) |
| { |
| // SourceCodeTextRanges are always constructed with raw resources and raw locations. |
| // However, we can provide the most accurate mapped locations in construction. |
| var startSourceCodeLocation = this.createSourceCodeLocation(textRange.startLine, textRange.startColumn); |
| var endSourceCodeLocation = this.createSourceCodeLocation(textRange.endLine, textRange.endColumn); |
| return new WI.SourceCodeTextRange(this._sourceMap.originalSourceCode, startSourceCodeLocation, endSourceCodeLocation); |
| } |
| }; |