OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2012 Google 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 are | |
6 * met: | |
7 * | |
8 * * Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * * Redistributions in binary form must reproduce the above | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 | |
31 /** | |
32 * @unrestricted | |
33 */ | |
34 Workspace.FileSystemMapping = class extends Common.Object { | |
35 /** | |
36 * @param {!Workspace.IsolatedFileSystemManager} fileSystemManager | |
37 */ | |
38 constructor(fileSystemManager) { | |
39 super(); | |
40 this._fileSystemMappingSetting = Common.settings.createLocalSetting('fileSys
temMapping', {}); | |
41 /** @type {!Object.<string, !Array.<!Workspace.FileSystemMapping.Entry>>} */ | |
42 this._fileSystemMappings = {}; | |
43 this._loadFromSettings(); | |
44 | |
45 this._eventListeners = [ | |
46 fileSystemManager.addEventListener( | |
47 Workspace.IsolatedFileSystemManager.Events.FileSystemAdded, this._file
SystemAdded, this), | |
48 fileSystemManager.addEventListener( | |
49 Workspace.IsolatedFileSystemManager.Events.FileSystemRemoved, this._fi
leSystemRemoved, this), | |
50 ]; | |
51 fileSystemManager.waitForFileSystems().then(this._fileSystemsLoaded.bind(thi
s)); | |
52 } | |
53 | |
54 /** | |
55 * @param {!Array<!Workspace.IsolatedFileSystem>} fileSystems | |
56 */ | |
57 _fileSystemsLoaded(fileSystems) { | |
58 for (var fileSystem of fileSystems) | |
59 this._addMappingsForFilesystem(fileSystem); | |
60 } | |
61 | |
62 /** | |
63 * @param {!Common.Event} event | |
64 */ | |
65 _fileSystemAdded(event) { | |
66 var fileSystem = /** @type {!Workspace.IsolatedFileSystem} */ (event.data); | |
67 this._addMappingsForFilesystem(fileSystem); | |
68 } | |
69 | |
70 /** | |
71 * @param {!Workspace.IsolatedFileSystem} fileSystem | |
72 */ | |
73 _addMappingsForFilesystem(fileSystem) { | |
74 this.addFileSystem(fileSystem.path()); | |
75 | |
76 var mappings = fileSystem.projectProperty('mappings'); | |
77 for (var i = 0; Array.isArray(mappings) && i < mappings.length; ++i) { | |
78 var mapping = mappings[i]; | |
79 if (!mapping || typeof mapping !== 'object') | |
80 continue; | |
81 var folder = mapping['folder']; | |
82 var url = mapping['url']; | |
83 if (typeof folder !== 'string' || typeof url !== 'string') | |
84 continue; | |
85 this.addNonConfigurableFileMapping(fileSystem.path(), url, folder); | |
86 } | |
87 } | |
88 | |
89 /** | |
90 * @param {!Common.Event} event | |
91 */ | |
92 _fileSystemRemoved(event) { | |
93 var fileSystem = /** @type {!Workspace.IsolatedFileSystem} */ (event.data); | |
94 this.removeFileSystem(fileSystem.path()); | |
95 } | |
96 | |
97 _loadFromSettings() { | |
98 var savedMapping = this._fileSystemMappingSetting.get(); | |
99 this._fileSystemMappings = {}; | |
100 for (var fileSystemPath in savedMapping) { | |
101 var savedFileSystemMappings = savedMapping[fileSystemPath]; | |
102 fileSystemPath = Common.ParsedURL.platformPathToURL(fileSystemPath); | |
103 this._fileSystemMappings[fileSystemPath] = []; | |
104 var fileSystemMappings = this._fileSystemMappings[fileSystemPath]; | |
105 | |
106 for (var i = 0; i < savedFileSystemMappings.length; ++i) { | |
107 var savedEntry = savedFileSystemMappings[i]; | |
108 var entry = | |
109 new Workspace.FileSystemMapping.Entry(fileSystemPath, savedEntry.url
Prefix, savedEntry.pathPrefix, true); | |
110 fileSystemMappings.push(entry); | |
111 } | |
112 } | |
113 | |
114 this._rebuildIndexes(); | |
115 } | |
116 | |
117 _saveToSettings() { | |
118 var setting = {}; | |
119 for (var fileSystemPath in this._fileSystemMappings) { | |
120 setting[fileSystemPath] = []; | |
121 var entries = this._fileSystemMappings[fileSystemPath]; | |
122 for (var entry of entries) { | |
123 if (entry.configurable) | |
124 setting[fileSystemPath].push(entry); | |
125 } | |
126 } | |
127 this._fileSystemMappingSetting.set(setting); | |
128 } | |
129 | |
130 _rebuildIndexes() { | |
131 // We are building an index here to search for the longest url prefix match
faster. | |
132 this._mappingForURLPrefix = {}; | |
133 this._urlPrefixes = []; | |
134 for (var fileSystemPath in this._fileSystemMappings) { | |
135 var fileSystemMapping = this._fileSystemMappings[fileSystemPath]; | |
136 for (var i = 0; i < fileSystemMapping.length; ++i) { | |
137 var entry = fileSystemMapping[i]; | |
138 // Resolve conflict in favor of configurable mapping. | |
139 if (this._mappingForURLPrefix[entry.urlPrefix] && !entry.configurable) | |
140 continue; | |
141 this._mappingForURLPrefix[entry.urlPrefix] = entry; | |
142 if (this._urlPrefixes.indexOf(entry.urlPrefix) === -1) | |
143 this._urlPrefixes.push(entry.urlPrefix); | |
144 } | |
145 } | |
146 this._urlPrefixes.sort(); | |
147 } | |
148 | |
149 /** | |
150 * @param {string} fileSystemPath | |
151 */ | |
152 addFileSystem(fileSystemPath) { | |
153 if (this._fileSystemMappings[fileSystemPath]) | |
154 return; | |
155 | |
156 this._fileSystemMappings[fileSystemPath] = []; | |
157 this._saveToSettings(); | |
158 } | |
159 | |
160 /** | |
161 * @param {string} fileSystemPath | |
162 */ | |
163 removeFileSystem(fileSystemPath) { | |
164 if (!this._fileSystemMappings[fileSystemPath]) | |
165 return; | |
166 delete this._fileSystemMappings[fileSystemPath]; | |
167 this._rebuildIndexes(); | |
168 this._saveToSettings(); | |
169 } | |
170 | |
171 /** | |
172 * @param {string} fileSystemPath | |
173 * @param {string} urlPrefix | |
174 * @param {string} pathPrefix | |
175 */ | |
176 addFileMapping(fileSystemPath, urlPrefix, pathPrefix) { | |
177 if (!urlPrefix.endsWith('/')) | |
178 urlPrefix += '/'; | |
179 if (!pathPrefix.endsWith('/')) | |
180 pathPrefix += '/'; | |
181 if (!pathPrefix.startsWith('/')) | |
182 pathPrefix = '/' + pathPrefix; | |
183 this._innerAddFileMapping(fileSystemPath, urlPrefix, pathPrefix, true); | |
184 this._saveToSettings(); | |
185 } | |
186 | |
187 /** | |
188 * @param {string} fileSystemPath | |
189 * @param {string} urlPrefix | |
190 * @param {string} pathPrefix | |
191 */ | |
192 addNonConfigurableFileMapping(fileSystemPath, urlPrefix, pathPrefix) { | |
193 this._innerAddFileMapping(fileSystemPath, urlPrefix, pathPrefix, false); | |
194 } | |
195 | |
196 /** | |
197 * @param {string} fileSystemPath | |
198 * @param {string} urlPrefix | |
199 * @param {string} pathPrefix | |
200 * @param {boolean} configurable | |
201 */ | |
202 _innerAddFileMapping(fileSystemPath, urlPrefix, pathPrefix, configurable) { | |
203 var entry = new Workspace.FileSystemMapping.Entry(fileSystemPath, urlPrefix,
pathPrefix, configurable); | |
204 this._fileSystemMappings[fileSystemPath].push(entry); | |
205 this._rebuildIndexes(); | |
206 this.dispatchEventToListeners(Workspace.FileSystemMapping.Events.FileMapping
Added, entry); | |
207 } | |
208 | |
209 /** | |
210 * @param {string} fileSystemPath | |
211 * @param {string} urlPrefix | |
212 * @param {string} pathPrefix | |
213 */ | |
214 removeFileMapping(fileSystemPath, urlPrefix, pathPrefix) { | |
215 var entry = this._configurableMappingEntryForPathPrefix(fileSystemPath, path
Prefix); | |
216 if (!entry) | |
217 return; | |
218 this._fileSystemMappings[fileSystemPath].remove(entry); | |
219 this._rebuildIndexes(); | |
220 this._saveToSettings(); | |
221 this.dispatchEventToListeners(Workspace.FileSystemMapping.Events.FileMapping
Removed, entry); | |
222 } | |
223 | |
224 /** | |
225 * @param {string} url | |
226 * @return {?Workspace.FileSystemMapping.Entry} | |
227 */ | |
228 _mappingEntryForURL(url) { | |
229 for (var i = this._urlPrefixes.length - 1; i >= 0; --i) { | |
230 var urlPrefix = this._urlPrefixes[i]; | |
231 if (url.startsWith(urlPrefix)) | |
232 return this._mappingForURLPrefix[urlPrefix]; | |
233 } | |
234 return null; | |
235 } | |
236 | |
237 /** | |
238 * @param {string} fileSystemPath | |
239 * @param {string} filePath | |
240 * @return {?Workspace.FileSystemMapping.Entry} | |
241 */ | |
242 _mappingEntryForPath(fileSystemPath, filePath) { | |
243 var entries = this._fileSystemMappings[fileSystemPath]; | |
244 if (!entries) | |
245 return null; | |
246 | |
247 var entry = null; | |
248 for (var i = 0; i < entries.length; ++i) { | |
249 var pathPrefix = entries[i].pathPrefix; | |
250 if (entry && entry.configurable && !entries[i].configurable) | |
251 continue; | |
252 // We are looking for the longest pathPrefix match. | |
253 if (entry && entry.pathPrefix.length > pathPrefix.length) | |
254 continue; | |
255 if (filePath.startsWith(pathPrefix)) | |
256 entry = entries[i]; | |
257 } | |
258 return entry; | |
259 } | |
260 | |
261 /** | |
262 * @param {string} fileSystemPath | |
263 * @param {string} pathPrefix | |
264 * @return {?Workspace.FileSystemMapping.Entry} | |
265 */ | |
266 _configurableMappingEntryForPathPrefix(fileSystemPath, pathPrefix) { | |
267 var entries = this._fileSystemMappings[fileSystemPath]; | |
268 for (var i = 0; i < entries.length; ++i) { | |
269 if (entries[i].configurable && pathPrefix === entries[i].pathPrefix) | |
270 return entries[i]; | |
271 } | |
272 return null; | |
273 } | |
274 | |
275 /** | |
276 * @param {string} fileSystemPath | |
277 * @return {!Array.<!Workspace.FileSystemMapping.Entry>} | |
278 */ | |
279 mappingEntries(fileSystemPath) { | |
280 return this._fileSystemMappings[fileSystemPath].slice(); | |
281 } | |
282 | |
283 /** | |
284 * @param {string} url | |
285 * @return {boolean} | |
286 */ | |
287 hasMappingForNetworkURL(url) { | |
288 return !!this._mappingEntryForURL(url); | |
289 } | |
290 | |
291 /** | |
292 * @param {string} url | |
293 * @return {?{fileSystemPath: string, fileURL: string}} | |
294 */ | |
295 fileForURL(url) { | |
296 var entry = this._mappingEntryForURL(url); | |
297 if (!entry) | |
298 return null; | |
299 var file = {}; | |
300 file.fileSystemPath = entry.fileSystemPath; | |
301 file.fileURL = entry.fileSystemPath + entry.pathPrefix + url.substr(entry.ur
lPrefix.length); | |
302 return file; | |
303 } | |
304 | |
305 /** | |
306 * @param {string} fileSystemPath | |
307 * @param {string} filePath | |
308 * @return {string} | |
309 */ | |
310 networkURLForFileSystemURL(fileSystemPath, filePath) { | |
311 var relativePath = filePath.substring(fileSystemPath.length); | |
312 var entry = this._mappingEntryForPath(fileSystemPath, relativePath); | |
313 if (!entry) | |
314 return ''; | |
315 return entry.urlPrefix + relativePath.substring(entry.pathPrefix.length); | |
316 } | |
317 | |
318 /** | |
319 * @param {string} url | |
320 */ | |
321 removeMappingForURL(url) { | |
322 var entry = this._mappingEntryForURL(url); | |
323 if (!entry || !entry.configurable) | |
324 return; | |
325 this._fileSystemMappings[entry.fileSystemPath].remove(entry); | |
326 this._saveToSettings(); | |
327 } | |
328 | |
329 /** | |
330 * @param {string} url | |
331 * @param {string} fileSystemPath | |
332 * @param {string} filePath | |
333 */ | |
334 addMappingForResource(url, fileSystemPath, filePath) { | |
335 var commonPathSuffixLength = 0; | |
336 for (var i = 0; i < filePath.length; ++i) { | |
337 var filePathCharacter = filePath[filePath.length - 1 - i]; | |
338 var urlCharacter = url[url.length - 1 - i]; | |
339 if (filePathCharacter !== urlCharacter) | |
340 break; | |
341 if (filePathCharacter === '/') | |
342 commonPathSuffixLength = i; | |
343 } | |
344 var from = fileSystemPath.length; | |
345 var to = filePath.length - commonPathSuffixLength; | |
346 var pathPrefix = filePath.substring(from, to); | |
347 var urlPrefix = url.substr(0, url.length - commonPathSuffixLength); | |
348 if (to >= from) | |
349 this.addFileMapping(fileSystemPath, urlPrefix, pathPrefix); | |
350 else | |
351 this.addFileMapping(fileSystemPath, urlPrefix + pathPrefix, '/'); | |
352 } | |
353 | |
354 resetForTesting() { | |
355 this._fileSystemMappings = {}; | |
356 } | |
357 | |
358 dispose() { | |
359 Common.EventTarget.removeEventListeners(this._eventListeners); | |
360 } | |
361 }; | |
362 | |
363 /** @enum {symbol} */ | |
364 Workspace.FileSystemMapping.Events = { | |
365 FileMappingAdded: Symbol('FileMappingAdded'), | |
366 FileMappingRemoved: Symbol('FileMappingRemoved') | |
367 }; | |
368 | |
369 /** | |
370 * @unrestricted | |
371 */ | |
372 Workspace.FileSystemMapping.Entry = class { | |
373 /** | |
374 * @param {string} fileSystemPath | |
375 * @param {string} urlPrefix | |
376 * @param {string} pathPrefix | |
377 * @param {boolean} configurable | |
378 */ | |
379 constructor(fileSystemPath, urlPrefix, pathPrefix, configurable) { | |
380 this.fileSystemPath = fileSystemPath; | |
381 this.urlPrefix = urlPrefix; | |
382 this.pathPrefix = pathPrefix; | |
383 this.configurable = configurable; | |
384 } | |
385 }; | |
386 | |
387 /** | |
388 * @type {!Workspace.FileSystemMapping} | |
389 */ | |
390 Workspace.fileSystemMapping; | |
OLD | NEW |