OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 | |
7 * @implements {WebInspector.TargetManager.Observer} | 5 * @implements {WebInspector.TargetManager.Observer} |
8 * @param {!WebInspector.TargetManager} targetManager | 6 * @unrestricted |
9 * @param {!WebInspector.Context} context | |
10 */ | 7 */ |
11 WebInspector.ExecutionContextSelector = function(targetManager, context) | 8 WebInspector.ExecutionContextSelector = class { |
12 { | 9 /** |
| 10 * @param {!WebInspector.TargetManager} targetManager |
| 11 * @param {!WebInspector.Context} context |
| 12 */ |
| 13 constructor(targetManager, context) { |
13 targetManager.observeTargets(this, WebInspector.Target.Capability.JS); | 14 targetManager.observeTargets(this, WebInspector.Target.Capability.JS); |
14 context.addFlavorChangeListener(WebInspector.ExecutionContext, this._executi
onContextChanged, this); | 15 context.addFlavorChangeListener(WebInspector.ExecutionContext, this._executi
onContextChanged, this); |
15 context.addFlavorChangeListener(WebInspector.Target, this._targetChanged, th
is); | 16 context.addFlavorChangeListener(WebInspector.Target, this._targetChanged, th
is); |
16 | 17 |
17 targetManager.addModelListener(WebInspector.RuntimeModel, WebInspector.Runti
meModel.Events.ExecutionContextCreated, this._onExecutionContextCreated, this); | 18 targetManager.addModelListener( |
18 targetManager.addModelListener(WebInspector.RuntimeModel, WebInspector.Runti
meModel.Events.ExecutionContextDestroyed, this._onExecutionContextDestroyed, thi
s); | 19 WebInspector.RuntimeModel, WebInspector.RuntimeModel.Events.ExecutionCon
textCreated, |
19 targetManager.addModelListener(WebInspector.RuntimeModel, WebInspector.Runti
meModel.Events.ExecutionContextOrderChanged, this._onExecutionContextOrderChange
d, this); | 20 this._onExecutionContextCreated, this); |
| 21 targetManager.addModelListener( |
| 22 WebInspector.RuntimeModel, WebInspector.RuntimeModel.Events.ExecutionCon
textDestroyed, |
| 23 this._onExecutionContextDestroyed, this); |
| 24 targetManager.addModelListener( |
| 25 WebInspector.RuntimeModel, WebInspector.RuntimeModel.Events.ExecutionCon
textOrderChanged, |
| 26 this._onExecutionContextOrderChanged, this); |
20 this._targetManager = targetManager; | 27 this._targetManager = targetManager; |
21 this._context = context; | 28 this._context = context; |
22 }; | 29 } |
23 | 30 |
24 WebInspector.ExecutionContextSelector.prototype = { | 31 /** |
25 | 32 * @param {!Element} proxyElement |
26 /** | 33 * @param {!Range} wordRange |
27 * @override | 34 * @param {boolean} force |
28 * @param {!WebInspector.Target} target | 35 * @param {function(!Array.<string>, number=)} completionsReadyCallback |
29 */ | 36 */ |
30 targetAdded: function(target) | 37 static completionsForTextPromptInCurrentContext(proxyElement, wordRange, force
, completionsReadyCallback) { |
31 { | |
32 // Defer selecting default target since we need all clients to get their | |
33 // targetAdded notifications first. | |
34 setImmediate(deferred.bind(this)); | |
35 | |
36 /** | |
37 * @this {WebInspector.ExecutionContextSelector} | |
38 */ | |
39 function deferred() | |
40 { | |
41 // We always want the second context for the service worker targets. | |
42 if (!this._context.flavor(WebInspector.Target)) | |
43 this._context.setFlavor(WebInspector.Target, target); | |
44 } | |
45 }, | |
46 | |
47 /** | |
48 * @override | |
49 * @param {!WebInspector.Target} target | |
50 */ | |
51 targetRemoved: function(target) | |
52 { | |
53 var currentExecutionContext = this._context.flavor(WebInspector.Executio
nContext); | |
54 if (currentExecutionContext && currentExecutionContext.target() === targ
et) | |
55 this._currentExecutionContextGone(); | |
56 | |
57 var targets = this._targetManager.targets(WebInspector.Target.Capability
.JS); | |
58 if (this._context.flavor(WebInspector.Target) === target && targets.leng
th) | |
59 this._context.setFlavor(WebInspector.Target, targets[0]); | |
60 }, | |
61 | |
62 /** | |
63 * @param {!WebInspector.Event} event | |
64 */ | |
65 _executionContextChanged: function(event) | |
66 { | |
67 var newContext = /** @type {?WebInspector.ExecutionContext} */ (event.da
ta); | |
68 if (newContext) { | |
69 this._context.setFlavor(WebInspector.Target, newContext.target()); | |
70 if (!this._ignoreContextChanged) | |
71 this._lastSelectedContextId = this._contextPersistentId(newConte
xt); | |
72 } | |
73 }, | |
74 | |
75 /** | |
76 * @param {!WebInspector.ExecutionContext} executionContext | |
77 * @return {string} | |
78 */ | |
79 _contextPersistentId: function(executionContext) | |
80 { | |
81 return executionContext.isDefault ? executionContext.target().name() + "
:" + executionContext.frameId : ""; | |
82 }, | |
83 | |
84 /** | |
85 * @param {!WebInspector.Event} event | |
86 */ | |
87 _targetChanged: function(event) | |
88 { | |
89 var newTarget = /** @type {?WebInspector.Target} */(event.data); | |
90 var currentContext = this._context.flavor(WebInspector.ExecutionContext)
; | |
91 | |
92 if (!newTarget || (currentContext && currentContext.target() === newTarg
et)) | |
93 return; | |
94 | |
95 var executionContexts = newTarget.runtimeModel.executionContexts(); | |
96 if (!executionContexts.length) | |
97 return; | |
98 | |
99 var newContext = null; | |
100 for (var i = 0; i < executionContexts.length && !newContext; ++i) { | |
101 if (this._shouldSwitchToContext(executionContexts[i])) | |
102 newContext = executionContexts[i]; | |
103 } | |
104 for (var i = 0; i < executionContexts.length && !newContext; ++i) { | |
105 if (this._isDefaultContext(executionContexts[i])) | |
106 newContext = executionContexts[i]; | |
107 } | |
108 this._ignoreContextChanged = true; | |
109 this._context.setFlavor(WebInspector.ExecutionContext, newContext || exe
cutionContexts[0]); | |
110 this._ignoreContextChanged = false; | |
111 }, | |
112 | |
113 /** | |
114 * @param {!WebInspector.ExecutionContext} executionContext | |
115 * @return {boolean} | |
116 */ | |
117 _shouldSwitchToContext: function(executionContext) | |
118 { | |
119 if (this._lastSelectedContextId && this._lastSelectedContextId === this.
_contextPersistentId(executionContext)) | |
120 return true; | |
121 if (!this._lastSelectedContextId && this._isDefaultContext(executionCont
ext)) | |
122 return true; | |
123 return false; | |
124 }, | |
125 | |
126 /** | |
127 * @param {!WebInspector.ExecutionContext} executionContext | |
128 * @return {boolean} | |
129 */ | |
130 _isDefaultContext: function(executionContext) | |
131 { | |
132 if (!executionContext.isDefault || !executionContext.frameId) | |
133 return false; | |
134 if (executionContext.target().parentTarget()) | |
135 return false; | |
136 var resourceTreeModel = WebInspector.ResourceTreeModel.fromTarget(execut
ionContext.target()); | |
137 var frame = resourceTreeModel && resourceTreeModel.frameForId(executionC
ontext.frameId); | |
138 if (frame && frame.isMainFrame()) | |
139 return true; | |
140 return false; | |
141 }, | |
142 | |
143 /** | |
144 * @param {!WebInspector.Event} event | |
145 */ | |
146 _onExecutionContextCreated: function(event) | |
147 { | |
148 this._switchContextIfNecessary(/** @type {!WebInspector.ExecutionContext
} */ (event.data)); | |
149 }, | |
150 | |
151 /** | |
152 * @param {!WebInspector.Event} event | |
153 */ | |
154 _onExecutionContextDestroyed: function(event) | |
155 { | |
156 var executionContext = /** @type {!WebInspector.ExecutionContext}*/ (eve
nt.data); | |
157 if (this._context.flavor(WebInspector.ExecutionContext) === executionCon
text) | |
158 this._currentExecutionContextGone(); | |
159 }, | |
160 | |
161 /** | |
162 * @param {!WebInspector.Event} event | |
163 */ | |
164 _onExecutionContextOrderChanged: function(event) | |
165 { | |
166 var runtimeModel = /** @type {!WebInspector.RuntimeModel} */ (event.data
); | |
167 var executionContexts = runtimeModel.executionContexts(); | |
168 for (var i = 0; i < executionContexts.length; i++) { | |
169 if (this._switchContextIfNecessary(executionContexts[i])) | |
170 break; | |
171 } | |
172 }, | |
173 | |
174 /** | |
175 * @param {!WebInspector.ExecutionContext} executionContext | |
176 * @return {boolean} | |
177 */ | |
178 _switchContextIfNecessary: function(executionContext) | |
179 { | |
180 if (!this._context.flavor(WebInspector.ExecutionContext) || this._should
SwitchToContext(executionContext)) { | |
181 this._ignoreContextChanged = true; | |
182 this._context.setFlavor(WebInspector.ExecutionContext, executionCont
ext); | |
183 this._ignoreContextChanged = false; | |
184 return true; | |
185 } | |
186 return false; | |
187 }, | |
188 | |
189 _currentExecutionContextGone: function() | |
190 { | |
191 var targets = this._targetManager.targets(WebInspector.Target.Capability
.JS); | |
192 var newContext = null; | |
193 for (var i = 0; i < targets.length && !newContext; ++i) { | |
194 var executionContexts = targets[i].runtimeModel.executionContexts(); | |
195 for (var executionContext of executionContexts) { | |
196 if (this._isDefaultContext(executionContext)) { | |
197 newContext = executionContext; | |
198 break; | |
199 } | |
200 } | |
201 } | |
202 if (!newContext) { | |
203 for (var i = 0; i < targets.length && !newContext; ++i) { | |
204 var executionContexts = targets[i].runtimeModel.executionContext
s(); | |
205 if (executionContexts.length) { | |
206 newContext = executionContexts[0]; | |
207 break; | |
208 } | |
209 } | |
210 } | |
211 this._ignoreContextChanged = true; | |
212 this._context.setFlavor(WebInspector.ExecutionContext, newContext); | |
213 this._ignoreContextChanged = false; | |
214 } | |
215 }; | |
216 | |
217 /** | |
218 * @param {!Element} proxyElement | |
219 * @param {!Range} wordRange | |
220 * @param {boolean} force | |
221 * @param {function(!Array.<string>, number=)} completionsReadyCallback | |
222 */ | |
223 WebInspector.ExecutionContextSelector.completionsForTextPromptInCurrentContext =
function(proxyElement, wordRange, force, completionsReadyCallback) | |
224 { | |
225 var expressionRange = wordRange.cloneRange(); | 38 var expressionRange = wordRange.cloneRange(); |
226 expressionRange.collapse(true); | 39 expressionRange.collapse(true); |
227 expressionRange.setStartBefore(proxyElement); | 40 expressionRange.setStartBefore(proxyElement); |
228 WebInspector.ExecutionContextSelector.completionsForTextInCurrentContext(exp
ressionRange.toString(), wordRange.toString(), force).then(completionsReadyCallb
ack); | 41 WebInspector.ExecutionContextSelector |
229 }; | 42 .completionsForTextInCurrentContext(expressionRange.toString(), wordRang
e.toString(), force) |
230 /** | 43 .then(completionsReadyCallback); |
231 * @param {string} text | 44 } |
232 * @param {string} completionsPrefix | 45 |
233 * @param {boolean=} force | 46 /** |
234 * @return {!Promise<!Array<string>>} | 47 * @param {string} text |
235 */ | 48 * @param {string} completionsPrefix |
236 WebInspector.ExecutionContextSelector.completionsForTextInCurrentContext = funct
ion(text, completionsPrefix, force) | 49 * @param {boolean=} force |
237 { | 50 * @return {!Promise<!Array<string>>} |
| 51 */ |
| 52 static completionsForTextInCurrentContext(text, completionsPrefix, force) { |
238 var executionContext = WebInspector.context.flavor(WebInspector.ExecutionCon
text); | 53 var executionContext = WebInspector.context.flavor(WebInspector.ExecutionCon
text); |
239 if (!executionContext) | 54 if (!executionContext) |
240 return Promise.resolve([]); | 55 return Promise.resolve([]); |
241 var index; | 56 var index; |
242 var stopChars = new Set(" =:({;,!+-*/&|^<>`".split("")); | 57 var stopChars = new Set(' =:({;,!+-*/&|^<>`'.split('')); |
243 for (index = text.length - 1; index >= 0; index--) { | 58 for (index = text.length - 1; index >= 0; index--) { |
244 // Pass less stop characters to rangeOfWord so the range will be a more
complete expression. | 59 // Pass less stop characters to rangeOfWord so the range will be a more co
mplete expression. |
245 if (stopChars.has(text.charAt(index))) | 60 if (stopChars.has(text.charAt(index))) |
246 break; | 61 break; |
247 } | 62 } |
248 var clippedExpression = text.substring(index + 1); | 63 var clippedExpression = text.substring(index + 1); |
249 var bracketCount = 0; | 64 var bracketCount = 0; |
250 | 65 |
251 index = clippedExpression.length - 1; | 66 index = clippedExpression.length - 1; |
252 while (index >= 0) { | 67 while (index >= 0) { |
253 var character = clippedExpression.charAt(index); | 68 var character = clippedExpression.charAt(index); |
254 if (character === "]") | 69 if (character === ']') |
255 bracketCount++; | 70 bracketCount++; |
256 // Allow an open bracket at the end for property completion. | 71 // Allow an open bracket at the end for property completion. |
257 if (character === "[" && index < clippedExpression.length - 1) { | 72 if (character === '[' && index < clippedExpression.length - 1) { |
258 bracketCount--; | 73 bracketCount--; |
259 if (bracketCount < 0) | 74 if (bracketCount < 0) |
260 break; | 75 break; |
| 76 } |
| 77 index--; |
| 78 } |
| 79 clippedExpression = clippedExpression.substring(index + 1); |
| 80 |
| 81 return executionContext.completionsForExpression(clippedExpression, completi
onsPrefix, force); |
| 82 } |
| 83 |
| 84 /** |
| 85 * @override |
| 86 * @param {!WebInspector.Target} target |
| 87 */ |
| 88 targetAdded(target) { |
| 89 // Defer selecting default target since we need all clients to get their |
| 90 // targetAdded notifications first. |
| 91 setImmediate(deferred.bind(this)); |
| 92 |
| 93 /** |
| 94 * @this {WebInspector.ExecutionContextSelector} |
| 95 */ |
| 96 function deferred() { |
| 97 // We always want the second context for the service worker targets. |
| 98 if (!this._context.flavor(WebInspector.Target)) |
| 99 this._context.setFlavor(WebInspector.Target, target); |
| 100 } |
| 101 } |
| 102 |
| 103 /** |
| 104 * @override |
| 105 * @param {!WebInspector.Target} target |
| 106 */ |
| 107 targetRemoved(target) { |
| 108 var currentExecutionContext = this._context.flavor(WebInspector.ExecutionCon
text); |
| 109 if (currentExecutionContext && currentExecutionContext.target() === target) |
| 110 this._currentExecutionContextGone(); |
| 111 |
| 112 var targets = this._targetManager.targets(WebInspector.Target.Capability.JS)
; |
| 113 if (this._context.flavor(WebInspector.Target) === target && targets.length) |
| 114 this._context.setFlavor(WebInspector.Target, targets[0]); |
| 115 } |
| 116 |
| 117 /** |
| 118 * @param {!WebInspector.Event} event |
| 119 */ |
| 120 _executionContextChanged(event) { |
| 121 var newContext = /** @type {?WebInspector.ExecutionContext} */ (event.data); |
| 122 if (newContext) { |
| 123 this._context.setFlavor(WebInspector.Target, newContext.target()); |
| 124 if (!this._ignoreContextChanged) |
| 125 this._lastSelectedContextId = this._contextPersistentId(newContext); |
| 126 } |
| 127 } |
| 128 |
| 129 /** |
| 130 * @param {!WebInspector.ExecutionContext} executionContext |
| 131 * @return {string} |
| 132 */ |
| 133 _contextPersistentId(executionContext) { |
| 134 return executionContext.isDefault ? executionContext.target().name() + ':' +
executionContext.frameId : ''; |
| 135 } |
| 136 |
| 137 /** |
| 138 * @param {!WebInspector.Event} event |
| 139 */ |
| 140 _targetChanged(event) { |
| 141 var newTarget = /** @type {?WebInspector.Target} */ (event.data); |
| 142 var currentContext = this._context.flavor(WebInspector.ExecutionContext); |
| 143 |
| 144 if (!newTarget || (currentContext && currentContext.target() === newTarget)) |
| 145 return; |
| 146 |
| 147 var executionContexts = newTarget.runtimeModel.executionContexts(); |
| 148 if (!executionContexts.length) |
| 149 return; |
| 150 |
| 151 var newContext = null; |
| 152 for (var i = 0; i < executionContexts.length && !newContext; ++i) { |
| 153 if (this._shouldSwitchToContext(executionContexts[i])) |
| 154 newContext = executionContexts[i]; |
| 155 } |
| 156 for (var i = 0; i < executionContexts.length && !newContext; ++i) { |
| 157 if (this._isDefaultContext(executionContexts[i])) |
| 158 newContext = executionContexts[i]; |
| 159 } |
| 160 this._ignoreContextChanged = true; |
| 161 this._context.setFlavor(WebInspector.ExecutionContext, newContext || executi
onContexts[0]); |
| 162 this._ignoreContextChanged = false; |
| 163 } |
| 164 |
| 165 /** |
| 166 * @param {!WebInspector.ExecutionContext} executionContext |
| 167 * @return {boolean} |
| 168 */ |
| 169 _shouldSwitchToContext(executionContext) { |
| 170 if (this._lastSelectedContextId && this._lastSelectedContextId === this._con
textPersistentId(executionContext)) |
| 171 return true; |
| 172 if (!this._lastSelectedContextId && this._isDefaultContext(executionContext)
) |
| 173 return true; |
| 174 return false; |
| 175 } |
| 176 |
| 177 /** |
| 178 * @param {!WebInspector.ExecutionContext} executionContext |
| 179 * @return {boolean} |
| 180 */ |
| 181 _isDefaultContext(executionContext) { |
| 182 if (!executionContext.isDefault || !executionContext.frameId) |
| 183 return false; |
| 184 if (executionContext.target().parentTarget()) |
| 185 return false; |
| 186 var resourceTreeModel = WebInspector.ResourceTreeModel.fromTarget(executionC
ontext.target()); |
| 187 var frame = resourceTreeModel && resourceTreeModel.frameForId(executionConte
xt.frameId); |
| 188 if (frame && frame.isMainFrame()) |
| 189 return true; |
| 190 return false; |
| 191 } |
| 192 |
| 193 /** |
| 194 * @param {!WebInspector.Event} event |
| 195 */ |
| 196 _onExecutionContextCreated(event) { |
| 197 this._switchContextIfNecessary(/** @type {!WebInspector.ExecutionContext} */
(event.data)); |
| 198 } |
| 199 |
| 200 /** |
| 201 * @param {!WebInspector.Event} event |
| 202 */ |
| 203 _onExecutionContextDestroyed(event) { |
| 204 var executionContext = /** @type {!WebInspector.ExecutionContext}*/ (event.d
ata); |
| 205 if (this._context.flavor(WebInspector.ExecutionContext) === executionContext
) |
| 206 this._currentExecutionContextGone(); |
| 207 } |
| 208 |
| 209 /** |
| 210 * @param {!WebInspector.Event} event |
| 211 */ |
| 212 _onExecutionContextOrderChanged(event) { |
| 213 var runtimeModel = /** @type {!WebInspector.RuntimeModel} */ (event.data); |
| 214 var executionContexts = runtimeModel.executionContexts(); |
| 215 for (var i = 0; i < executionContexts.length; i++) { |
| 216 if (this._switchContextIfNecessary(executionContexts[i])) |
| 217 break; |
| 218 } |
| 219 } |
| 220 |
| 221 /** |
| 222 * @param {!WebInspector.ExecutionContext} executionContext |
| 223 * @return {boolean} |
| 224 */ |
| 225 _switchContextIfNecessary(executionContext) { |
| 226 if (!this._context.flavor(WebInspector.ExecutionContext) || this._shouldSwit
chToContext(executionContext)) { |
| 227 this._ignoreContextChanged = true; |
| 228 this._context.setFlavor(WebInspector.ExecutionContext, executionContext); |
| 229 this._ignoreContextChanged = false; |
| 230 return true; |
| 231 } |
| 232 return false; |
| 233 } |
| 234 |
| 235 _currentExecutionContextGone() { |
| 236 var targets = this._targetManager.targets(WebInspector.Target.Capability.JS)
; |
| 237 var newContext = null; |
| 238 for (var i = 0; i < targets.length && !newContext; ++i) { |
| 239 var executionContexts = targets[i].runtimeModel.executionContexts(); |
| 240 for (var executionContext of executionContexts) { |
| 241 if (this._isDefaultContext(executionContext)) { |
| 242 newContext = executionContext; |
| 243 break; |
261 } | 244 } |
262 index--; | 245 } |
263 } | 246 } |
264 clippedExpression = clippedExpression.substring(index + 1); | 247 if (!newContext) { |
265 | 248 for (var i = 0; i < targets.length && !newContext; ++i) { |
266 return executionContext.completionsForExpression(clippedExpression, completi
onsPrefix, force); | 249 var executionContexts = targets[i].runtimeModel.executionContexts(); |
| 250 if (executionContexts.length) { |
| 251 newContext = executionContexts[0]; |
| 252 break; |
| 253 } |
| 254 } |
| 255 } |
| 256 this._ignoreContextChanged = true; |
| 257 this._context.setFlavor(WebInspector.ExecutionContext, newContext); |
| 258 this._ignoreContextChanged = false; |
| 259 } |
267 }; | 260 }; |
| 261 |
| 262 |
OLD | NEW |