1 /* 2 * Copyright (C) 2013 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 * @constructor 33 * @implements {WebInspector.ProjectDelegate} 34 * @extends {WebInspector.Object} 35 * @param {string} type 36 */ 37 WebInspector.ContentProviderBasedProjectDelegate = function(type) 38 { 39 this._type = type; 40 /** @type {!Object.<string, !WebInspector.ContentProvider>} */ 41 this._contentProviders = {}; 42 /** @type {!Object.<string, boolean>} */ 43 this._isContentScriptMap = {}; 44 } 45 46 WebInspector.ContentProviderBasedProjectDelegate.prototype = { 47 /** 48 * @return {string} 49 */ 50 id: function() 51 { 52 // Overriddden by subclasses 53 return ""; 54 }, 55 56 /** 57 * @return {string} 58 */ 59 type: function() 60 { 61 return this._type; 62 }, 63 64 /** 65 * @return {string} 66 */ 67 displayName: function() 68 { 69 // Overriddden by subclasses 70 return ""; 71 }, 72 73 /** 74 * @param {string} path 75 * @param {function(?Date, ?number)} callback 76 */ 77 requestMetadata: function(path, callback) 78 { 79 callback(null, null); 80 }, 81 82 /** 83 * @param {string} path 84 * @param {function(?string)} callback 85 */ 86 requestFileContent: function(path, callback) 87 { 88 var contentProvider = this._contentProviders[path]; 89 contentProvider.requestContent(callback); 90 91 /** 92 * @param {?string} content 93 * @param {boolean} encoded 94 * @param {string} mimeType 95 */ 96 function innerCallback(content, encoded, mimeType) 97 { 98 callback(content); 99 } 100 }, 101 102 /** 103 * @return {boolean} 104 */ 105 canSetFileContent: function() 106 { 107 return false; 108 }, 109 110 /** 111 * @param {string} path 112 * @param {string} newContent 113 * @param {function(?string)} callback 114 */ 115 setFileContent: function(path, newContent, callback) 116 { 117 callback(null); 118 }, 119 120 /** 121 * @return {boolean} 122 */ 123 canRename: function() 124 { 125 return false; 126 }, 127 128 /** 129 * @param {string} path 130 * @param {string} newName 131 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback 132 */ 133 rename: function(path, newName, callback) 134 { 135 this.performRename(path, newName, innerCallback.bind(this)); 136 137 /** 138 * @param {boolean} success 139 * @param {string=} newName 140 * @this {WebInspector.ContentProviderBasedProjectDelegate} 141 */ 142 function innerCallback(success, newName) 143 { 144 if (success) 145 this._updateName(path, /** @type {string} */ (newName)); 146 callback(success, newName); 147 } 148 }, 149 150 /** 151 * @param {string} path 152 */ 153 refresh: function(path) 154 { 155 }, 156 157 /** 158 * @param {string} path 159 */ 160 excludeFolder: function(path) 161 { 162 }, 163 164 /** 165 * @param {string} path 166 * @param {?string} name 167 * @param {string} content 168 * @param {function(?string)} callback 169 */ 170 createFile: function(path, name, content, callback) 171 { 172 }, 173 174 /** 175 * @param {string} path 176 */ 177 deleteFile: function(path) 178 { 179 }, 180 181 remove: function() 182 { 183 }, 184 185 /** 186 * @param {string} path 187 * @param {string} newName 188 * @param {function(boolean, string=)} callback 189 */ 190 performRename: function(path, newName, callback) 191 { 192 callback(false); 193 }, 194 195 /** 196 * @param {string} path 197 * @param {string} newName 198 */ 199 _updateName: function(path, newName) 200 { 201 var oldPath = path; 202 var copyOfPath = path.split("/"); 203 copyOfPath[copyOfPath.length - 1] = newName; 204 var newPath = copyOfPath.join("/"); 205 this._contentProviders[newPath] = this._contentProviders[oldPath]; 206 delete this._contentProviders[oldPath]; 207 }, 208 209 /** 210 * @param {string} path 211 * @param {string} query 212 * @param {boolean} caseSensitive 213 * @param {boolean} isRegex 214 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback 215 */ 216 searchInFileContent: function(path, query, caseSensitive, isRegex, callback) 217 { 218 var contentProvider = this._contentProviders[path]; 219 contentProvider.searchInContent(query, caseSensitive, isRegex, callback); 220 }, 221 222 /** 223 * @param {!Array.<string>} queries 224 * @param {!Array.<string>} fileQueries 225 * @param {boolean} caseSensitive 226 * @param {boolean} isRegex 227 * @param {!WebInspector.Progress} progress 228 * @param {function(!Array.<string>)} callback 229 */ 230 findFilesMatchingSearchRequest: function(queries, fileQueries, caseSensitive, isRegex, progress, callback) 231 { 232 var result = []; 233 var paths = Object.keys(this._contentProviders); 234 var totalCount = paths.length; 235 if (totalCount === 0) { 236 // searchInContent should call back later. 237 setTimeout(doneCallback, 0); 238 return; 239 } 240 241 /** 242 * @param {string} path 243 * @this {WebInspector.ContentProviderBasedProjectDelegate} 244 */ 245 function filterOutContentScripts(path) 246 { 247 return !this._isContentScriptMap[path]; 248 } 249 250 if (!WebInspector.settings.searchInContentScripts.get()) 251 paths = paths.filter(filterOutContentScripts.bind(this)); 252 253 var fileRegexes = []; 254 for (var i = 0; i < fileQueries.length; ++i) 255 fileRegexes.push(new RegExp(fileQueries[i], caseSensitive ? "" : "i")); 256 257 /** 258 * @param {!string} file 259 */ 260 function filterOutNonMatchingFiles(file) 261 { 262 for (var i = 0; i < fileRegexes.length; ++i) { 263 if (!file.match(fileRegexes[i])) 264 return false; 265 } 266 return true; 267 } 268 269 paths = paths.filter(filterOutNonMatchingFiles); 270 var barrier = new CallbackBarrier(); 271 progress.setTotalWork(paths.length); 272 for (var i = 0; i < paths.length; ++i) 273 searchInContent.call(this, paths[i], barrier.createCallback(searchInContentCallback.bind(this, paths[i]))); 274 barrier.callWhenDone(doneCallback); 275 276 /** 277 * @param {string} path 278 * @param {function(boolean)} callback 279 * @this {WebInspector.ContentProviderBasedProjectDelegate} 280 */ 281 function searchInContent(path, callback) 282 { 283 var queriesToRun = queries.slice(); 284 searchNextQuery.call(this); 285 286 /** 287 * @this {WebInspector.ContentProviderBasedProjectDelegate} 288 */ 289 function searchNextQuery() 290 { 291 if (!queriesToRun.length) { 292 callback(true); 293 return; 294 } 295 var query = queriesToRun.shift(); 296 this._contentProviders[path].searchInContent(query, caseSensitive, isRegex, contentCallback.bind(this)); 297 } 298 299 /** 300 * @param {!Array.<!WebInspector.ContentProvider.SearchMatch>} searchMatches 301 * @this {WebInspector.ContentProviderBasedProjectDelegate} 302 */ 303 function contentCallback(searchMatches) 304 { 305 if (!searchMatches.length) { 306 callback(false); 307 return; 308 } 309 searchNextQuery.call(this); 310 } 311 } 312 313 /** 314 * @param {string} path 315 * @param {boolean} matches 316 */ 317 function searchInContentCallback(path, matches) 318 { 319 if (matches) 320 result.push(path); 321 progress.worked(1); 322 } 323 324 function doneCallback() 325 { 326 callback(result); 327 progress.done(); 328 } 329 }, 330 331 /** 332 * @param {!WebInspector.Progress} progress 333 * @param {function()} callback 334 */ 335 indexContent: function(progress, callback) 336 { 337 setTimeout(innerCallback, 0); 338 339 function innerCallback() 340 { 341 progress.done(); 342 callback(); 343 } 344 }, 345 346 /** 347 * @param {string} parentPath 348 * @param {string} name 349 * @param {string} url 350 * @param {!WebInspector.ContentProvider} contentProvider 351 * @param {boolean} isEditable 352 * @param {boolean=} isContentScript 353 * @return {string} 354 */ 355 addContentProvider: function(parentPath, name, url, contentProvider, isEditable, isContentScript) 356 { 357 var path = parentPath ? parentPath + "/" + name : name; 358 var fileDescriptor = new WebInspector.FileDescriptor(parentPath, name, url, url, contentProvider.contentType(), isEditable, isContentScript); 359 this._contentProviders[path] = contentProvider; 360 this._isContentScriptMap[path] = isContentScript || false; 361 this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.FileAdded, fileDescriptor); 362 return path; 363 }, 364 365 /** 366 * @param {string} path 367 */ 368 removeFile: function(path) 369 { 370 delete this._contentProviders[path]; 371 delete this._isContentScriptMap[path]; 372 this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.FileRemoved, path); 373 }, 374 375 /** 376 * @return {!Object.<string, !WebInspector.ContentProvider>} 377 */ 378 contentProviders: function() 379 { 380 return this._contentProviders; 381 }, 382 383 reset: function() 384 { 385 this._contentProviders = {}; 386 this._isContentScriptMap = {}; 387 this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.Reset, null); 388 }, 389 390 __proto__: WebInspector.Object.prototype 391 } 392