OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | |
5 /** | 4 /** |
6 * @constructor | 5 * @unrestricted |
7 * @extends {WebInspector.Object} | |
8 * @param {!WebInspector.Workspace} workspace | |
9 * @param {!WebInspector.BreakpointManager} breakpointManager | |
10 * @param {!WebInspector.FileSystemMapping} fileSystemMapping | |
11 */ | 6 */ |
12 WebInspector.Persistence = function(workspace, breakpointManager, fileSystemMapp
ing) | 7 WebInspector.Persistence = class extends WebInspector.Object { |
13 { | 8 /** |
14 WebInspector.Object.call(this); | 9 * @param {!WebInspector.Workspace} workspace |
| 10 * @param {!WebInspector.BreakpointManager} breakpointManager |
| 11 * @param {!WebInspector.FileSystemMapping} fileSystemMapping |
| 12 */ |
| 13 constructor(workspace, breakpointManager, fileSystemMapping) { |
| 14 super(); |
15 this._workspace = workspace; | 15 this._workspace = workspace; |
16 this._breakpointManager = breakpointManager; | 16 this._breakpointManager = breakpointManager; |
17 /** @type {!Map<string, number>} */ | 17 /** @type {!Map<string, number>} */ |
18 this._filePathPrefixesToBindingCount = new Map(); | 18 this._filePathPrefixesToBindingCount = new Map(); |
19 | 19 |
20 if (Runtime.experiments.isEnabled("persistence2")) | 20 if (Runtime.experiments.isEnabled('persistence2')) |
21 this._mapping = new WebInspector.Automapping(workspace, this._onBindingC
reated.bind(this), this._onBindingRemoved.bind(this)); | 21 this._mapping = |
| 22 new WebInspector.Automapping(workspace, this._onBindingCreated.bind(th
is), this._onBindingRemoved.bind(this)); |
22 else | 23 else |
23 this._mapping = new WebInspector.DefaultMapping(workspace, fileSystemMap
ping, this._onBindingCreated.bind(this), this._onBindingRemoved.bind(this)); | 24 this._mapping = new WebInspector.DefaultMapping( |
| 25 workspace, fileSystemMapping, this._onBindingCreated.bind(this), this.
_onBindingRemoved.bind(this)); |
| 26 } |
| 27 |
| 28 /** |
| 29 * @param {!WebInspector.PersistenceBinding} binding |
| 30 */ |
| 31 _onBindingCreated(binding) { |
| 32 if (binding.network.isDirty()) { |
| 33 WebInspector.console.log(WebInspector.UIString( |
| 34 '%s can not be persisted to file system due to unsaved changes.', bind
ing.network.name())); |
| 35 return; |
| 36 } |
| 37 if (binding.fileSystem.isDirty()) |
| 38 binding.network.setWorkingCopy(binding.fileSystem.workingCopy()); |
| 39 |
| 40 binding.network[WebInspector.Persistence._binding] = binding; |
| 41 binding.fileSystem[WebInspector.Persistence._binding] = binding; |
| 42 |
| 43 binding.fileSystem.forceLoadOnCheckContent(); |
| 44 |
| 45 binding.network.addEventListener( |
| 46 WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCo
pyCommitted, this); |
| 47 binding.fileSystem.addEventListener( |
| 48 WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCo
pyCommitted, this); |
| 49 |
| 50 this._addFilePathBindingPrefixes(binding.fileSystem.url()); |
| 51 |
| 52 this._moveBreakpoints(binding.fileSystem, binding.network); |
| 53 this.dispatchEventToListeners(WebInspector.Persistence.Events.BindingCreated
, binding); |
| 54 } |
| 55 |
| 56 /** |
| 57 * @param {!WebInspector.PersistenceBinding} binding |
| 58 */ |
| 59 _onBindingRemoved(binding) { |
| 60 if (binding.network.isDirty()) |
| 61 binding.fileSystem.setWorkingCopy(binding.network.workingCopy()); |
| 62 |
| 63 binding.network[WebInspector.Persistence._binding] = null; |
| 64 binding.fileSystem[WebInspector.Persistence._binding] = null; |
| 65 |
| 66 binding.network.removeEventListener( |
| 67 WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCo
pyCommitted, this); |
| 68 binding.fileSystem.removeEventListener( |
| 69 WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCo
pyCommitted, this); |
| 70 |
| 71 this._removeFilePathBindingPrefixes(binding.fileSystem.url()); |
| 72 |
| 73 this._copyBreakpoints(binding.network, binding.fileSystem); |
| 74 this.dispatchEventToListeners(WebInspector.Persistence.Events.BindingRemoved
, binding); |
| 75 } |
| 76 |
| 77 /** |
| 78 * @param {!WebInspector.Event} event |
| 79 */ |
| 80 _onWorkingCopyCommitted(event) { |
| 81 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); |
| 82 var binding = uiSourceCode[WebInspector.Persistence._binding]; |
| 83 if (!binding || binding[WebInspector.Persistence._muteCommit]) |
| 84 return; |
| 85 var newContent = /** @type {string} */ (event.data.content); |
| 86 var other = binding.network === uiSourceCode ? binding.fileSystem : binding.
network; |
| 87 var target = WebInspector.NetworkProject.targetForUISourceCode(binding.netwo
rk); |
| 88 if (target.isNodeJS()) { |
| 89 other.requestContent().then( |
| 90 currentContent => this._syncNodeJSContent(binding, other, currentConte
nt, newContent)); |
| 91 return; |
| 92 } |
| 93 binding[WebInspector.Persistence._muteCommit] = true; |
| 94 other.addRevision(newContent); |
| 95 binding[WebInspector.Persistence._muteCommit] = false; |
| 96 this._contentSyncedForTest(); |
| 97 } |
| 98 |
| 99 /** |
| 100 * @param {!WebInspector.PersistenceBinding} binding |
| 101 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 102 * @param {string} currentContent |
| 103 * @param {string} newContent |
| 104 */ |
| 105 _syncNodeJSContent(binding, uiSourceCode, currentContent, newContent) { |
| 106 if (uiSourceCode === binding.fileSystem) { |
| 107 if (newContent.startsWith(WebInspector.Persistence._NodePrefix) && |
| 108 newContent.endsWith(WebInspector.Persistence._NodeSuffix)) |
| 109 newContent = newContent.substring( |
| 110 WebInspector.Persistence._NodePrefix.length, |
| 111 newContent.length - WebInspector.Persistence._NodeSuffix.length); |
| 112 if (currentContent.startsWith(WebInspector.Persistence._NodeShebang)) |
| 113 newContent = WebInspector.Persistence._NodeShebang + newContent; |
| 114 } else { |
| 115 if (newContent.startsWith(WebInspector.Persistence._NodeShebang)) |
| 116 newContent = newContent.substring(WebInspector.Persistence._NodeShebang.
length); |
| 117 if (currentContent.startsWith(WebInspector.Persistence._NodePrefix) && |
| 118 currentContent.endsWith(WebInspector.Persistence._NodeSuffix)) |
| 119 newContent = WebInspector.Persistence._NodePrefix + newContent + WebInsp
ector.Persistence._NodeSuffix; |
| 120 } |
| 121 binding[WebInspector.Persistence._muteCommit] = true; |
| 122 uiSourceCode.addRevision(newContent); |
| 123 binding[WebInspector.Persistence._muteCommit] = false; |
| 124 this._contentSyncedForTest(); |
| 125 } |
| 126 |
| 127 _contentSyncedForTest() { |
| 128 } |
| 129 |
| 130 /** |
| 131 * @param {!WebInspector.UISourceCode} from |
| 132 * @param {!WebInspector.UISourceCode} to |
| 133 */ |
| 134 _moveBreakpoints(from, to) { |
| 135 var breakpoints = this._breakpointManager.breakpointsForUISourceCode(from); |
| 136 for (var breakpoint of breakpoints) { |
| 137 breakpoint.remove(); |
| 138 this._breakpointManager.setBreakpoint( |
| 139 to, breakpoint.lineNumber(), breakpoint.columnNumber(), breakpoint.con
dition(), breakpoint.enabled()); |
| 140 } |
| 141 } |
| 142 |
| 143 /** |
| 144 * @param {!WebInspector.UISourceCode} from |
| 145 * @param {!WebInspector.UISourceCode} to |
| 146 */ |
| 147 _copyBreakpoints(from, to) { |
| 148 var breakpoints = this._breakpointManager.breakpointsForUISourceCode(from); |
| 149 for (var breakpoint of breakpoints) |
| 150 this._breakpointManager.setBreakpoint( |
| 151 to, breakpoint.lineNumber(), breakpoint.columnNumber(), breakpoint.con
dition(), breakpoint.enabled()); |
| 152 } |
| 153 |
| 154 /** |
| 155 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 156 * @return {boolean} |
| 157 */ |
| 158 hasUnsavedCommittedChanges(uiSourceCode) { |
| 159 if (this._workspace.hasResourceContentTrackingExtensions()) |
| 160 return false; |
| 161 if (uiSourceCode.url() && WebInspector.fileManager.isURLSaved(uiSourceCode.u
rl())) |
| 162 return false; |
| 163 if (uiSourceCode.project().canSetFileContent()) |
| 164 return false; |
| 165 if (uiSourceCode[WebInspector.Persistence._binding]) |
| 166 return false; |
| 167 return !!uiSourceCode.history.length; |
| 168 } |
| 169 |
| 170 /** |
| 171 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 172 * @return {?WebInspector.PersistenceBinding} |
| 173 */ |
| 174 binding(uiSourceCode) { |
| 175 return uiSourceCode[WebInspector.Persistence._binding] || null; |
| 176 } |
| 177 |
| 178 /** |
| 179 * @param {!WebInspector.UISourceCode} uiSourceCode |
| 180 * @return {?WebInspector.UISourceCode} |
| 181 */ |
| 182 fileSystem(uiSourceCode) { |
| 183 var binding = this.binding(uiSourceCode); |
| 184 return binding ? binding.fileSystem : null; |
| 185 } |
| 186 |
| 187 /** |
| 188 * @param {string} filePath |
| 189 */ |
| 190 _addFilePathBindingPrefixes(filePath) { |
| 191 var relative = ''; |
| 192 for (var token of filePath.split('/')) { |
| 193 relative += token + '/'; |
| 194 var count = this._filePathPrefixesToBindingCount.get(relative) || 0; |
| 195 this._filePathPrefixesToBindingCount.set(relative, count + 1); |
| 196 } |
| 197 } |
| 198 |
| 199 /** |
| 200 * @param {string} filePath |
| 201 */ |
| 202 _removeFilePathBindingPrefixes(filePath) { |
| 203 var relative = ''; |
| 204 for (var token of filePath.split('/')) { |
| 205 relative += token + '/'; |
| 206 var count = this._filePathPrefixesToBindingCount.get(relative); |
| 207 if (count === 1) |
| 208 this._filePathPrefixesToBindingCount.delete(relative); |
| 209 else |
| 210 this._filePathPrefixesToBindingCount.set(relative, count - 1); |
| 211 } |
| 212 } |
| 213 |
| 214 /** |
| 215 * @param {string} filePath |
| 216 * @return {boolean} |
| 217 */ |
| 218 filePathHasBindings(filePath) { |
| 219 if (!filePath.endsWith('/')) |
| 220 filePath += '/'; |
| 221 return this._filePathPrefixesToBindingCount.has(filePath); |
| 222 } |
| 223 |
| 224 dispose() { |
| 225 this._mapping.dispose(); |
| 226 } |
24 }; | 227 }; |
25 | 228 |
26 WebInspector.Persistence._binding = Symbol("Persistence.Binding"); | 229 WebInspector.Persistence._binding = Symbol('Persistence.Binding'); |
27 WebInspector.Persistence._muteCommit = Symbol("Persistence.MuteCommit"); | 230 WebInspector.Persistence._muteCommit = Symbol('Persistence.MuteCommit'); |
28 | 231 |
29 WebInspector.Persistence._NodePrefix = "(function (exports, require, module, __f
ilename, __dirname) { "; | 232 WebInspector.Persistence._NodePrefix = '(function (exports, require, module, __f
ilename, __dirname) { '; |
30 WebInspector.Persistence._NodeSuffix = "\n});"; | 233 WebInspector.Persistence._NodeSuffix = '\n});'; |
31 WebInspector.Persistence._NodeShebang = "#!/usr/bin/env node\n"; | 234 WebInspector.Persistence._NodeShebang = '#!/usr/bin/env node\n'; |
32 | 235 |
33 WebInspector.Persistence.Events = { | 236 WebInspector.Persistence.Events = { |
34 BindingCreated: Symbol("BindingCreated"), | 237 BindingCreated: Symbol('BindingCreated'), |
35 BindingRemoved: Symbol("BindingRemoved") | 238 BindingRemoved: Symbol('BindingRemoved') |
36 }; | 239 }; |
37 | 240 |
38 WebInspector.Persistence.prototype = { | |
39 /** | |
40 * @param {!WebInspector.PersistenceBinding} binding | |
41 */ | |
42 _onBindingCreated: function(binding) | |
43 { | |
44 if (binding.network.isDirty()) { | |
45 WebInspector.console.log(WebInspector.UIString("%s can not be persis
ted to file system due to unsaved changes.", binding.network.name())); | |
46 return; | |
47 } | |
48 if (binding.fileSystem.isDirty()) | |
49 binding.network.setWorkingCopy(binding.fileSystem.workingCopy()); | |
50 | |
51 binding.network[WebInspector.Persistence._binding] = binding; | |
52 binding.fileSystem[WebInspector.Persistence._binding] = binding; | |
53 | |
54 binding.fileSystem.forceLoadOnCheckContent(); | |
55 | |
56 binding.network.addEventListener(WebInspector.UISourceCode.Events.Workin
gCopyCommitted, this._onWorkingCopyCommitted, this); | |
57 binding.fileSystem.addEventListener(WebInspector.UISourceCode.Events.Wor
kingCopyCommitted, this._onWorkingCopyCommitted, this); | |
58 | |
59 this._addFilePathBindingPrefixes(binding.fileSystem.url()); | |
60 | |
61 this._moveBreakpoints(binding.fileSystem, binding.network); | |
62 this.dispatchEventToListeners(WebInspector.Persistence.Events.BindingCre
ated, binding); | |
63 }, | |
64 | |
65 /** | |
66 * @param {!WebInspector.PersistenceBinding} binding | |
67 */ | |
68 _onBindingRemoved: function(binding) | |
69 { | |
70 if (binding.network.isDirty()) | |
71 binding.fileSystem.setWorkingCopy(binding.network.workingCopy()); | |
72 | |
73 binding.network[WebInspector.Persistence._binding] = null; | |
74 binding.fileSystem[WebInspector.Persistence._binding] = null; | |
75 | |
76 binding.network.removeEventListener(WebInspector.UISourceCode.Events.Wor
kingCopyCommitted, this._onWorkingCopyCommitted, this); | |
77 binding.fileSystem.removeEventListener(WebInspector.UISourceCode.Events.
WorkingCopyCommitted, this._onWorkingCopyCommitted, this); | |
78 | |
79 this._removeFilePathBindingPrefixes(binding.fileSystem.url()); | |
80 | |
81 this._copyBreakpoints(binding.network, binding.fileSystem); | |
82 this.dispatchEventToListeners(WebInspector.Persistence.Events.BindingRem
oved, binding); | |
83 }, | |
84 | |
85 /** | |
86 * @param {!WebInspector.Event} event | |
87 */ | |
88 _onWorkingCopyCommitted: function(event) | |
89 { | |
90 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.targe
t); | |
91 var binding = uiSourceCode[WebInspector.Persistence._binding]; | |
92 if (!binding || binding[WebInspector.Persistence._muteCommit]) | |
93 return; | |
94 var newContent = /** @type {string} */(event.data.content); | |
95 var other = binding.network === uiSourceCode ? binding.fileSystem : bind
ing.network; | |
96 var target = WebInspector.NetworkProject.targetForUISourceCode(binding.n
etwork); | |
97 if (target.isNodeJS()) { | |
98 other.requestContent().then(currentContent => this._syncNodeJSConten
t(binding, other, currentContent, newContent)); | |
99 return; | |
100 } | |
101 binding[WebInspector.Persistence._muteCommit] = true; | |
102 other.addRevision(newContent); | |
103 binding[WebInspector.Persistence._muteCommit] = false; | |
104 this._contentSyncedForTest(); | |
105 }, | |
106 | |
107 /** | |
108 * @param {!WebInspector.PersistenceBinding} binding | |
109 * @param {!WebInspector.UISourceCode} uiSourceCode | |
110 * @param {string} currentContent | |
111 * @param {string} newContent | |
112 */ | |
113 _syncNodeJSContent: function(binding, uiSourceCode, currentContent, newConte
nt) | |
114 { | |
115 if (uiSourceCode === binding.fileSystem) { | |
116 if (newContent.startsWith(WebInspector.Persistence._NodePrefix) && n
ewContent.endsWith(WebInspector.Persistence._NodeSuffix)) | |
117 newContent = newContent.substring(WebInspector.Persistence._Node
Prefix.length, newContent.length - WebInspector.Persistence._NodeSuffix.length); | |
118 if (currentContent.startsWith(WebInspector.Persistence._NodeShebang)
) | |
119 newContent = WebInspector.Persistence._NodeShebang + newContent; | |
120 } else { | |
121 if (newContent.startsWith(WebInspector.Persistence._NodeShebang)) | |
122 newContent = newContent.substring(WebInspector.Persistence._Node
Shebang.length); | |
123 if (currentContent.startsWith(WebInspector.Persistence._NodePrefix)
&& currentContent.endsWith(WebInspector.Persistence._NodeSuffix)) | |
124 newContent = WebInspector.Persistence._NodePrefix + newContent +
WebInspector.Persistence._NodeSuffix; | |
125 } | |
126 binding[WebInspector.Persistence._muteCommit] = true; | |
127 uiSourceCode.addRevision(newContent); | |
128 binding[WebInspector.Persistence._muteCommit] = false; | |
129 this._contentSyncedForTest(); | |
130 }, | |
131 | |
132 _contentSyncedForTest: function() { }, | |
133 | |
134 /** | |
135 * @param {!WebInspector.UISourceCode} from | |
136 * @param {!WebInspector.UISourceCode} to | |
137 */ | |
138 _moveBreakpoints: function(from, to) | |
139 { | |
140 var breakpoints = this._breakpointManager.breakpointsForUISourceCode(fro
m); | |
141 for (var breakpoint of breakpoints) { | |
142 breakpoint.remove(); | |
143 this._breakpointManager.setBreakpoint(to, breakpoint.lineNumber(), b
reakpoint.columnNumber(), breakpoint.condition(), breakpoint.enabled()); | |
144 } | |
145 }, | |
146 | |
147 /** | |
148 * @param {!WebInspector.UISourceCode} from | |
149 * @param {!WebInspector.UISourceCode} to | |
150 */ | |
151 _copyBreakpoints: function(from, to) | |
152 { | |
153 var breakpoints = this._breakpointManager.breakpointsForUISourceCode(fro
m); | |
154 for (var breakpoint of breakpoints) | |
155 this._breakpointManager.setBreakpoint(to, breakpoint.lineNumber(), b
reakpoint.columnNumber(), breakpoint.condition(), breakpoint.enabled()); | |
156 }, | |
157 | |
158 /** | |
159 * @param {!WebInspector.UISourceCode} uiSourceCode | |
160 * @return {boolean} | |
161 */ | |
162 hasUnsavedCommittedChanges: function(uiSourceCode) | |
163 { | |
164 if (this._workspace.hasResourceContentTrackingExtensions()) | |
165 return false; | |
166 if (uiSourceCode.url() && WebInspector.fileManager.isURLSaved(uiSourceCo
de.url())) | |
167 return false; | |
168 if (uiSourceCode.project().canSetFileContent()) | |
169 return false; | |
170 if (uiSourceCode[WebInspector.Persistence._binding]) | |
171 return false; | |
172 return !!uiSourceCode.history.length; | |
173 }, | |
174 | |
175 /** | |
176 * @param {!WebInspector.UISourceCode} uiSourceCode | |
177 * @return {?WebInspector.PersistenceBinding} | |
178 */ | |
179 binding: function(uiSourceCode) | |
180 { | |
181 return uiSourceCode[WebInspector.Persistence._binding] || null; | |
182 }, | |
183 | |
184 /** | |
185 * @param {!WebInspector.UISourceCode} uiSourceCode | |
186 * @return {?WebInspector.UISourceCode} | |
187 */ | |
188 fileSystem: function(uiSourceCode) | |
189 { | |
190 var binding = this.binding(uiSourceCode); | |
191 return binding ? binding.fileSystem : null; | |
192 }, | |
193 | |
194 /** | |
195 * @param {string} filePath | |
196 */ | |
197 _addFilePathBindingPrefixes: function(filePath) | |
198 { | |
199 var relative = ""; | |
200 for (var token of filePath.split("/")) { | |
201 relative += token + "/"; | |
202 var count = this._filePathPrefixesToBindingCount.get(relative) || 0; | |
203 this._filePathPrefixesToBindingCount.set(relative, count + 1); | |
204 } | |
205 }, | |
206 | |
207 /** | |
208 * @param {string} filePath | |
209 */ | |
210 _removeFilePathBindingPrefixes: function(filePath) | |
211 { | |
212 var relative = ""; | |
213 for (var token of filePath.split("/")) { | |
214 relative += token + "/"; | |
215 var count = this._filePathPrefixesToBindingCount.get(relative); | |
216 if (count === 1) | |
217 this._filePathPrefixesToBindingCount.delete(relative); | |
218 else | |
219 this._filePathPrefixesToBindingCount.set(relative, count - 1); | |
220 } | |
221 }, | |
222 | |
223 /** | |
224 * @param {string} filePath | |
225 * @return {boolean} | |
226 */ | |
227 filePathHasBindings: function(filePath) | |
228 { | |
229 if (!filePath.endsWith("/")) | |
230 filePath += "/"; | |
231 return this._filePathPrefixesToBindingCount.has(filePath); | |
232 }, | |
233 | |
234 dispose: function() | |
235 { | |
236 this._mapping.dispose(); | |
237 }, | |
238 | |
239 __proto__: WebInspector.Object.prototype | |
240 }; | |
241 | |
242 /** | 241 /** |
243 * @constructor | 242 * @unrestricted |
244 * @param {!WebInspector.UISourceCode} network | |
245 * @param {!WebInspector.UISourceCode} fileSystem | |
246 * @param {boolean} exactMatch | |
247 */ | 243 */ |
248 WebInspector.PersistenceBinding = function(network, fileSystem, exactMatch) | 244 WebInspector.PersistenceBinding = class { |
249 { | 245 /** |
| 246 * @param {!WebInspector.UISourceCode} network |
| 247 * @param {!WebInspector.UISourceCode} fileSystem |
| 248 * @param {boolean} exactMatch |
| 249 */ |
| 250 constructor(network, fileSystem, exactMatch) { |
250 this.network = network; | 251 this.network = network; |
251 this.fileSystem = fileSystem; | 252 this.fileSystem = fileSystem; |
252 this.exactMatch = exactMatch; | 253 this.exactMatch = exactMatch; |
| 254 } |
253 }; | 255 }; |
254 | 256 |
255 /** @type {!WebInspector.Persistence} */ | 257 /** @type {!WebInspector.Persistence} */ |
256 WebInspector.persistence; | 258 WebInspector.persistence; |
OLD | NEW |