1/* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26WebInspector.SourceMapResource = function(url, sourceMap) 27{ 28 WebInspector.Resource.call(this, url, null); 29 30 console.assert(url); 31 console.assert(sourceMap); 32 33 this._sourceMap = sourceMap; 34 this._contentRequested = false; 35 36 var inheritedMIMEType = this._sourceMap.originalSourceCode instanceof WebInspector.Resource ? this._sourceMap.originalSourceCode.syntheticMIMEType : null; 37 38 var fileExtension = WebInspector.fileExtensionForURL(url); 39 var fileExtensionMIMEType = WebInspector.mimeTypeForFileExtension(fileExtension, true); 40 41 // FIXME: This is a layering violation. It should use a helper function on the 42 // Resource base-class to set _mimeType and _type. 43 this._mimeType = fileExtensionMIMEType || inheritedMIMEType || "text/javascript"; 44 this._type = WebInspector.Resource.Type.fromMIMEType(this._mimeType); 45 46 // Mark the resource as loaded so it does not show a spinner in the sidebar. 47 // We will really load the resource the first time content is requested. 48 this.markAsFinished(); 49}; 50 51WebInspector.SourceMapResource.prototype = { 52 constructor: WebInspector.SourceMapResource, 53 54 // Public 55 56 get sourceMap() 57 { 58 return this._sourceMap; 59 }, 60 61 get sourceMapDisplaySubpath() 62 { 63 var sourceMappingBasePathURLComponents = this._sourceMap.sourceMappingBasePathURLComponents; 64 var resourceURLComponents = this.urlComponents; 65 66 // Different schemes / hosts. Return the host + path of this resource. 67 if (resourceURLComponents.scheme !== sourceMappingBasePathURLComponents.scheme || resourceURLComponents.host !== sourceMappingBasePathURLComponents.host) 68 return resourceURLComponents.host + (resourceURLComponents.port ? (":" + resourceURLComponents.port) : "") + resourceURLComponents.path; 69 70 // Same host, but not a subpath of the base. This implies a ".." in the relative path. 71 if (!resourceURLComponents.path.startsWith(sourceMappingBasePathURLComponents.path)) 72 return relativePath(resourceURLComponents.path, sourceMappingBasePathURLComponents.path); 73 74 // Same host. Just a subpath of the base. 75 return resourceURLComponents.path.substring(sourceMappingBasePathURLComponents.path.length, resourceURLComponents.length); 76 }, 77 78 canRequestContentFromBackend: function() 79 { 80 return !this._contentRequested; 81 }, 82 83 requestContentFromBackend: function(callback) 84 { 85 this._contentRequested = true; 86 87 // Revert the markAsFinished that was done in the constructor. 88 this.revertMarkAsFinished(); 89 90 var inlineContent = this._sourceMap.sourceContent(this.url); 91 if (inlineContent) { 92 // Force inline content to be asynchronous to match the expected load pattern. 93 setTimeout(function() { 94 // FIXME: We don't know the MIME-type for inline content. Guess by analyzing the content? 95 sourceMapResourceLoaded.call(this, null, inlineContent, this.mimeType, 200); 96 }.bind(this)); 97 98 return true; 99 } 100 101 function sourceMapResourceLoaded(error, body, mimeType, statusCode) 102 { 103 const base64encoded = false; 104 105 if (error || statusCode >= 400) { 106 this.markAsFailed(); 107 callback(error, body, base64encoded); 108 return; 109 } 110 111 // FIXME: Add support for picking the best MIME-type. Right now the file extension is the best bet. 112 // The constructor set MIME-type based on the file extension and we ignore mimeType here. 113 114 this.markAsFinished(); 115 116 callback(null, body, base64encoded); 117 } 118 119 if (!NetworkAgent.loadResource) { 120 sourceMapResourceLoaded.call(this, "error: no NetworkAgent.loadResource"); 121 return false; 122 } 123 124 var frameIdentifier = null; 125 if (this._sourceMap.originalSourceCode instanceof WebInspector.Resource && this._sourceMap.originalSourceCode.parentFrame) 126 frameIdentifier = this._sourceMap.originalSourceCode.parentFrame.id; 127 128 if (!frameIdentifier) 129 frameIdentifier = WebInspector.frameResourceManager.mainFrame.id; 130 131 NetworkAgent.loadResource(frameIdentifier, this.url, sourceMapResourceLoaded.bind(this)); 132 133 return true; 134 }, 135 136 createSourceCodeLocation: function(lineNumber, columnNumber) 137 { 138 // SourceCodeLocations are always constructed with raw resources and raw locations. Lookup the raw location. 139 var entry = this._sourceMap.findEntryReversed(this.url, lineNumber); 140 var rawLineNumber = entry[0]; 141 var rawColumnNumber = entry[1]; 142 143 // If the raw location is an inline script we need to include that offset. 144 var originalSourceCode = this._sourceMap.originalSourceCode; 145 if (originalSourceCode instanceof WebInspector.Script) { 146 if (rawLineNumber === 0) 147 rawColumnNumber += originalSourceCode.range.startColumn; 148 rawLineNumber += originalSourceCode.range.startLine; 149 } 150 151 // Create the SourceCodeLocation and since we already know the the mapped location set it directly. 152 var location = originalSourceCode.createSourceCodeLocation(rawLineNumber, rawColumnNumber); 153 location._setMappedLocation(this, lineNumber, columnNumber); 154 return location; 155 }, 156 157 createSourceCodeTextRange: function(textRange) 158 { 159 // SourceCodeTextRanges are always constructed with raw resources and raw locations. 160 // However, we can provide the most accurate mapped locations in construction. 161 var startSourceCodeLocation = this.createSourceCodeLocation(textRange.startLine, textRange.startColumn); 162 var endSourceCodeLocation = this.createSourceCodeLocation(textRange.endLine, textRange.endColumn); 163 return new WebInspector.SourceCodeTextRange(this._sourceMap.originalSourceCode, startSourceCodeLocation, endSourceCodeLocation); 164 } 165}; 166 167WebInspector.SourceMapResource.prototype.__proto__ = WebInspector.Resource.prototype; 168