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