Workspace.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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. * @constructor
  32. * @param {string} parentPath
  33. * @param {string} name
  34. * @param {string} originURL
  35. * @param {string} url
  36. * @param {WebInspector.ResourceType} contentType
  37. * @param {boolean} isEditable
  38. * @param {boolean=} isContentScript
  39. */
  40. WebInspector.FileDescriptor = function(parentPath, name, originURL, url, contentType, isEditable, isContentScript)
  41. {
  42. this.parentPath = parentPath;
  43. this.name = name;
  44. this.originURL = originURL;
  45. this.url = url;
  46. this.contentType = contentType;
  47. this.isEditable = isEditable;
  48. this.isContentScript = isContentScript || false;
  49. }
  50. /**
  51. * @interface
  52. * @extends {WebInspector.EventTarget}
  53. */
  54. WebInspector.ProjectDelegate = function() { }
  55. WebInspector.ProjectDelegate.Events = {
  56. FileAdded: "FileAdded",
  57. FileRemoved: "FileRemoved",
  58. Reset: "Reset",
  59. }
  60. WebInspector.ProjectDelegate.prototype = {
  61. /**
  62. * @return {string}
  63. */
  64. id: function() { },
  65. /**
  66. * @return {string}
  67. */
  68. type: function() { },
  69. /**
  70. * @return {string}
  71. */
  72. displayName: function() { },
  73. /**
  74. * @param {string} path
  75. * @param {function(?Date, ?number)} callback
  76. */
  77. requestMetadata: function(path, callback) { },
  78. /**
  79. * @param {string} path
  80. * @param {function(?string,boolean,string)} callback
  81. */
  82. requestFileContent: function(path, callback) { },
  83. /**
  84. * @return {boolean}
  85. */
  86. canSetFileContent: function() { },
  87. /**
  88. * @param {string} path
  89. * @param {string} newContent
  90. * @param {function(?string)} callback
  91. */
  92. setFileContent: function(path, newContent, callback) { },
  93. /**
  94. * @return {boolean}
  95. */
  96. canRename: function() { },
  97. /**
  98. * @param {string} path
  99. * @param {string} newName
  100. * @param {function(boolean, string=)} callback
  101. */
  102. rename: function(path, newName, callback) { },
  103. /**
  104. * @param {string} path
  105. */
  106. refresh: function(path) { },
  107. /**
  108. * @param {string} path
  109. */
  110. excludeFolder: function(path) { },
  111. /**
  112. * @param {string} path
  113. * @param {?string} name
  114. * @param {function(?string)} callback
  115. */
  116. createFile: function(path, name, callback) { },
  117. /**
  118. * @param {string} path
  119. */
  120. deleteFile: function(path) { },
  121. remove: function() { },
  122. /**
  123. * @param {string} path
  124. * @param {string} query
  125. * @param {boolean} caseSensitive
  126. * @param {boolean} isRegex
  127. * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
  128. */
  129. searchInFileContent: function(path, query, caseSensitive, isRegex, callback) { },
  130. /**
  131. * @param {string} query
  132. * @param {boolean} caseSensitive
  133. * @param {boolean} isRegex
  134. * @param {WebInspector.Progress} progress
  135. * @param {function(StringMap)} callback
  136. */
  137. searchInContent: function(query, caseSensitive, isRegex, progress, callback) { },
  138. /**
  139. * @param {WebInspector.Progress} progress
  140. * @param {function()} callback
  141. */
  142. indexContent: function(progress, callback) { }
  143. }
  144. /**
  145. * @param {WebInspector.Workspace} workspace
  146. * @param {WebInspector.ProjectDelegate} projectDelegate
  147. * @constructor
  148. */
  149. WebInspector.Project = function(workspace, projectDelegate)
  150. {
  151. /** @type {Object.<string, {uiSourceCode: WebInspector.UISourceCode, index: number}>} */
  152. this._uiSourceCodesMap = {};
  153. /** @type {Array.<WebInspector.UISourceCode>} */
  154. this._uiSourceCodesList = [];
  155. this._workspace = workspace;
  156. this._projectDelegate = projectDelegate;
  157. this._displayName = this._projectDelegate.displayName();
  158. this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileAdded, this._fileAdded, this);
  159. this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileRemoved, this._fileRemoved, this);
  160. this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.Reset, this._reset, this);
  161. }
  162. WebInspector.Project.prototype = {
  163. /**
  164. * @return {string}
  165. */
  166. id: function()
  167. {
  168. return this._projectDelegate.id();
  169. },
  170. /**
  171. * @return {string}
  172. */
  173. type: function()
  174. {
  175. return this._projectDelegate.type();
  176. },
  177. /**
  178. * @return {string}
  179. */
  180. displayName: function()
  181. {
  182. return this._displayName;
  183. },
  184. /**
  185. * @return {boolean}
  186. */
  187. isServiceProject: function()
  188. {
  189. return this._projectDelegate.type() === WebInspector.projectTypes.Debugger || this._projectDelegate.type() === WebInspector.projectTypes.LiveEdit;
  190. },
  191. _fileAdded: function(event)
  192. {
  193. var fileDescriptor = /** @type {WebInspector.FileDescriptor} */ (event.data);
  194. var path = fileDescriptor.parentPath ? fileDescriptor.parentPath + "/" + fileDescriptor.name : fileDescriptor.name;
  195. var uiSourceCode = this.uiSourceCode(path);
  196. if (uiSourceCode)
  197. return;
  198. uiSourceCode = new WebInspector.UISourceCode(this, fileDescriptor.parentPath, fileDescriptor.name, fileDescriptor.originURL, fileDescriptor.url, fileDescriptor.contentType, fileDescriptor.isEditable);
  199. uiSourceCode.isContentScript = fileDescriptor.isContentScript;
  200. this._uiSourceCodesMap[path] = {uiSourceCode: uiSourceCode, index: this._uiSourceCodesList.length};
  201. this._uiSourceCodesList.push(uiSourceCode);
  202. this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeAdded, uiSourceCode);
  203. },
  204. _fileRemoved: function(event)
  205. {
  206. var path = /** @type {string} */ (event.data);
  207. this._removeFile(path);
  208. },
  209. /**
  210. * @param {string} path
  211. */
  212. _removeFile: function(path)
  213. {
  214. var uiSourceCode = this.uiSourceCode(path);
  215. if (!uiSourceCode)
  216. return;
  217. var entry = this._uiSourceCodesMap[path];
  218. var movedUISourceCode = this._uiSourceCodesList[this._uiSourceCodesList.length - 1];
  219. this._uiSourceCodesList[entry.index] = movedUISourceCode;
  220. var movedEntry = this._uiSourceCodesMap[movedUISourceCode.path()];
  221. movedEntry.index = entry.index;
  222. this._uiSourceCodesList.splice(this._uiSourceCodesList.length - 1, 1);
  223. delete this._uiSourceCodesMap[path];
  224. this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeRemoved, entry.uiSourceCode);
  225. },
  226. _reset: function()
  227. {
  228. this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectWillReset, this);
  229. this._uiSourceCodesMap = {};
  230. this._uiSourceCodesList = [];
  231. },
  232. /**
  233. * @param {string} path
  234. * @return {?WebInspector.UISourceCode}
  235. */
  236. uiSourceCode: function(path)
  237. {
  238. var entry = this._uiSourceCodesMap[path];
  239. return entry ? entry.uiSourceCode : null;
  240. },
  241. /**
  242. * @param {string} originURL
  243. * @return {?WebInspector.UISourceCode}
  244. */
  245. uiSourceCodeForOriginURL: function(originURL)
  246. {
  247. for (var i = 0; i < this._uiSourceCodesList.length; ++i) {
  248. var uiSourceCode = this._uiSourceCodesList[i];
  249. if (uiSourceCode.originURL() === originURL)
  250. return uiSourceCode;
  251. }
  252. return null;
  253. },
  254. /**
  255. * @return {Array.<WebInspector.UISourceCode>}
  256. */
  257. uiSourceCodes: function()
  258. {
  259. return this._uiSourceCodesList;
  260. },
  261. /**
  262. * @param {WebInspector.UISourceCode} uiSourceCode
  263. * @param {function(?Date, ?number)} callback
  264. */
  265. requestMetadata: function(uiSourceCode, callback)
  266. {
  267. this._projectDelegate.requestMetadata(uiSourceCode.path(), callback);
  268. },
  269. /**
  270. * @param {WebInspector.UISourceCode} uiSourceCode
  271. * @param {function(?string,boolean,string)} callback
  272. */
  273. requestFileContent: function(uiSourceCode, callback)
  274. {
  275. this._projectDelegate.requestFileContent(uiSourceCode.path(), callback);
  276. },
  277. /**
  278. * @return {boolean}
  279. */
  280. canSetFileContent: function()
  281. {
  282. return this._projectDelegate.canSetFileContent();
  283. },
  284. /**
  285. * @param {WebInspector.UISourceCode} uiSourceCode
  286. * @param {string} newContent
  287. * @param {function(?string)} callback
  288. */
  289. setFileContent: function(uiSourceCode, newContent, callback)
  290. {
  291. this._projectDelegate.setFileContent(uiSourceCode.path(), newContent, onSetContent.bind(this));
  292. /**
  293. * @param {?string} content
  294. */
  295. function onSetContent(content)
  296. {
  297. this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: uiSourceCode, content: newContent });
  298. callback(content);
  299. }
  300. },
  301. /**
  302. * @return {boolean}
  303. */
  304. canRename: function()
  305. {
  306. return this._projectDelegate.canRename();
  307. },
  308. /**
  309. * @param {WebInspector.UISourceCode} uiSourceCode
  310. * @param {string} newName
  311. * @param {function(boolean, string=)} callback
  312. */
  313. rename: function(uiSourceCode, newName, callback)
  314. {
  315. if (newName === uiSourceCode.name()) {
  316. callback(true, newName);
  317. return;
  318. }
  319. this._projectDelegate.rename(uiSourceCode.path(), newName, innerCallback.bind(this));
  320. /**
  321. * @param {boolean} success
  322. * @param {string=} newName
  323. */
  324. function innerCallback(success, newName)
  325. {
  326. if (!success || !newName) {
  327. callback(false);
  328. return;
  329. }
  330. var oldPath = uiSourceCode.path();
  331. var newPath = uiSourceCode.parentPath() ? uiSourceCode.parentPath() + "/" + newName : newName;
  332. this._uiSourceCodesMap[newPath] = this._uiSourceCodesMap[oldPath];
  333. delete this._uiSourceCodesMap[oldPath];
  334. callback(true, newName);
  335. }
  336. },
  337. /**
  338. * @param {string} path
  339. */
  340. refresh: function(path)
  341. {
  342. this._projectDelegate.refresh(path);
  343. },
  344. /**
  345. * @param {string} path
  346. */
  347. excludeFolder: function(path)
  348. {
  349. this._projectDelegate.excludeFolder(path);
  350. var uiSourceCodes = this._uiSourceCodesList.slice();
  351. for (var i = 0; i < uiSourceCodes.length; ++i) {
  352. var uiSourceCode = uiSourceCodes[i];
  353. if (uiSourceCode.path().startsWith(path.substr(1)))
  354. this._removeFile(uiSourceCode.path());
  355. }
  356. },
  357. /**
  358. * @param {string} path
  359. * @param {?string} name
  360. * @param {function(?string)} callback
  361. */
  362. createFile: function(path, name, callback)
  363. {
  364. this._projectDelegate.createFile(path, name, innerCallback);
  365. function innerCallback(filePath)
  366. {
  367. callback(filePath);
  368. }
  369. },
  370. /**
  371. * @param {WebInspector.UISourceCode} uiSourceCode
  372. */
  373. deleteFile: function(uiSourceCode)
  374. {
  375. this._projectDelegate.deleteFile(uiSourceCode.path());
  376. },
  377. remove: function()
  378. {
  379. this._projectDelegate.remove();
  380. },
  381. /**
  382. * @param {WebInspector.UISourceCode} uiSourceCode
  383. * @param {string} query
  384. * @param {boolean} caseSensitive
  385. * @param {boolean} isRegex
  386. * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
  387. */
  388. searchInFileContent: function(uiSourceCode, query, caseSensitive, isRegex, callback)
  389. {
  390. this._projectDelegate.searchInFileContent(uiSourceCode.path(), query, caseSensitive, isRegex, callback);
  391. },
  392. /**
  393. * @param {string} query
  394. * @param {boolean} caseSensitive
  395. * @param {boolean} isRegex
  396. * @param {WebInspector.Progress} progress
  397. * @param {function(StringMap)} callback
  398. */
  399. searchInContent: function(query, caseSensitive, isRegex, progress, callback)
  400. {
  401. this._projectDelegate.searchInContent(query, caseSensitive, isRegex, progress, callback);
  402. },
  403. /**
  404. * @param {WebInspector.Progress} progress
  405. * @param {function()} callback
  406. */
  407. indexContent: function(progress, callback)
  408. {
  409. this._projectDelegate.indexContent(progress, callback);
  410. },
  411. dispose: function()
  412. {
  413. this._projectDelegate.reset();
  414. }
  415. }
  416. WebInspector.projectTypes = {
  417. Debugger: "debugger",
  418. LiveEdit: "liveedit",
  419. Network: "network",
  420. Snippets: "snippets",
  421. FileSystem: "filesystem"
  422. }
  423. /**
  424. * @constructor
  425. * @extends {WebInspector.Object}
  426. * @param {WebInspector.FileSystemMapping} fileSystemMapping
  427. */
  428. WebInspector.Workspace = function(fileSystemMapping)
  429. {
  430. this._fileSystemMapping = fileSystemMapping;
  431. /** @type {!Object.<string, WebInspector.Project>} */
  432. this._projects = {};
  433. }
  434. WebInspector.Workspace.Events = {
  435. UISourceCodeAdded: "UISourceCodeAdded",
  436. UISourceCodeRemoved: "UISourceCodeRemoved",
  437. UISourceCodeContentCommitted: "UISourceCodeContentCommitted",
  438. ProjectWillReset: "ProjectWillReset"
  439. }
  440. WebInspector.Workspace.prototype = {
  441. /**
  442. * @param {string} projectId
  443. * @param {string} path
  444. * @return {?WebInspector.UISourceCode}
  445. */
  446. uiSourceCode: function(projectId, path)
  447. {
  448. var project = this._projects[projectId];
  449. return project ? project.uiSourceCode(path) : null;
  450. },
  451. /**
  452. * @param {string} originURL
  453. * @return {?WebInspector.UISourceCode}
  454. */
  455. uiSourceCodeForOriginURL: function(originURL)
  456. {
  457. var networkProjects = this.projectsForType(WebInspector.projectTypes.Network)
  458. for (var i = 0; i < networkProjects.length; ++i) {
  459. var project = networkProjects[i];
  460. var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
  461. if (uiSourceCode)
  462. return uiSourceCode;
  463. }
  464. return null;
  465. },
  466. /**
  467. * @param {string} type
  468. * @return {Array.<WebInspector.UISourceCode>}
  469. */
  470. uiSourceCodesForProjectType: function(type)
  471. {
  472. var result = [];
  473. for (var projectName in this._projects) {
  474. var project = this._projects[projectName];
  475. if (project.type() === type)
  476. result = result.concat(project.uiSourceCodes());
  477. }
  478. return result;
  479. },
  480. /**
  481. * @param {WebInspector.ProjectDelegate} projectDelegate
  482. * @return {WebInspector.Project}
  483. */
  484. addProject: function(projectDelegate)
  485. {
  486. var projectId = projectDelegate.id();
  487. this._projects[projectId] = new WebInspector.Project(this, projectDelegate);
  488. return this._projects[projectId];
  489. },
  490. /**
  491. * @param {string} projectId
  492. */
  493. removeProject: function(projectId)
  494. {
  495. var project = this._projects[projectId];
  496. if (!project)
  497. return;
  498. project.dispose();
  499. delete this._projects[projectId];
  500. },
  501. /**
  502. * @param {string} projectId
  503. * @return {WebInspector.Project}
  504. */
  505. project: function(projectId)
  506. {
  507. return this._projects[projectId];
  508. },
  509. /**
  510. * @return {Array.<WebInspector.Project>}
  511. */
  512. projects: function()
  513. {
  514. return Object.values(this._projects);
  515. },
  516. /**
  517. * @param {string} type
  518. * @return {Array.<WebInspector.Project>}
  519. */
  520. projectsForType: function(type)
  521. {
  522. function filterByType(project)
  523. {
  524. return project.type() === type;
  525. }
  526. return this.projects().filter(filterByType);
  527. },
  528. /**
  529. * @return {Array.<WebInspector.UISourceCode>}
  530. */
  531. uiSourceCodes: function()
  532. {
  533. var result = [];
  534. for (var projectId in this._projects) {
  535. var project = this._projects[projectId];
  536. result = result.concat(project.uiSourceCodes());
  537. }
  538. return result;
  539. },
  540. /**
  541. * @param {string} url
  542. * @return {boolean}
  543. */
  544. hasMappingForURL: function(url)
  545. {
  546. if (!InspectorFrontendHost.supportsFileSystems())
  547. return false;
  548. return this._fileSystemMapping.hasMappingForURL(url);
  549. },
  550. /**
  551. * @param {string} url
  552. * @return {WebInspector.UISourceCode}
  553. */
  554. _networkUISourceCodeForURL: function(url)
  555. {
  556. var splitURL = WebInspector.ParsedURL.splitURL(url);
  557. var projectId = WebInspector.SimpleProjectDelegate.projectId(splitURL[0], WebInspector.projectTypes.Network);
  558. var project = this.project(projectId);
  559. return project ? project.uiSourceCode(splitURL.slice(1).join("/")) : null;
  560. },
  561. /**
  562. * @param {string} url
  563. * @return {WebInspector.UISourceCode}
  564. */
  565. uiSourceCodeForURL: function(url)
  566. {
  567. if (!InspectorFrontendHost.supportsFileSystems())
  568. return this._networkUISourceCodeForURL(url);
  569. var file = this._fileSystemMapping.fileForURL(url);
  570. if (!file)
  571. return this._networkUISourceCodeForURL(url);
  572. var projectId = WebInspector.FileSystemProjectDelegate.projectId(file.fileSystemPath);
  573. var project = this.project(projectId);
  574. return project ? project.uiSourceCode(file.filePath) : null;
  575. },
  576. /**
  577. * @param {string} fileSystemPath
  578. * @param {string} filePath
  579. * @return {string}
  580. */
  581. urlForPath: function(fileSystemPath, filePath)
  582. {
  583. return this._fileSystemMapping.urlForPath(fileSystemPath, filePath);
  584. },
  585. /**
  586. * @param {WebInspector.UISourceCode} networkUISourceCode
  587. * @param {WebInspector.UISourceCode} uiSourceCode
  588. * @param {WebInspector.FileSystemWorkspaceProvider} fileSystemWorkspaceProvider
  589. */
  590. addMapping: function(networkUISourceCode, uiSourceCode, fileSystemWorkspaceProvider)
  591. {
  592. var url = networkUISourceCode.url;
  593. var path = uiSourceCode.path();
  594. var fileSystemPath = fileSystemWorkspaceProvider.fileSystemPath(uiSourceCode);
  595. this._fileSystemMapping.addMappingForResource(url, fileSystemPath, path);
  596. WebInspector.suggestReload();
  597. },
  598. /**
  599. * @param {WebInspector.UISourceCode} uiSourceCode
  600. */
  601. removeMapping: function(uiSourceCode)
  602. {
  603. this._fileSystemMapping.removeMappingForURL(uiSourceCode.url);
  604. WebInspector.suggestReload();
  605. },
  606. __proto__: WebInspector.Object.prototype
  607. }
  608. /**
  609. * @type {?WebInspector.Workspace}
  610. */
  611. WebInspector.workspace = null;