Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(532)

Side by Side Diff: plugins/org.chromium.sdk.wipbackend.wk118685/src/org/chromium/sdk/internal/wip/WipScriptManager.java

Issue 11829027: drop old backends (Closed) Base URL: https://chromedevtools.googlecode.com/svn/trunk
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.sdk.internal.wip;
6
7 import static org.chromium.sdk.util.BasicUtil.containsKeySafe;
8 import static org.chromium.sdk.util.BasicUtil.getSafe;
9
10 import java.util.ArrayDeque;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Queue;
17 import java.util.Set;
18
19 import org.chromium.sdk.DebugEventListener;
20 import org.chromium.sdk.JavascriptVm;
21 import org.chromium.sdk.RelayOk;
22 import org.chromium.sdk.Script;
23 import org.chromium.sdk.SyncCallback;
24 import org.chromium.sdk.internal.ScriptBase;
25 import org.chromium.sdk.internal.wip.protocol.input.debugger.GetScriptSourceData ;
26 import org.chromium.sdk.internal.wip.protocol.input.debugger.ScriptParsedEventDa ta;
27 import org.chromium.sdk.internal.wip.protocol.output.debugger.GetScriptSourcePar ams;
28 import org.chromium.sdk.util.AsyncFuture;
29 import org.chromium.sdk.util.AsyncFuture.Callback;
30 import org.chromium.sdk.util.AsyncFutureMerger;
31 import org.chromium.sdk.util.AsyncFutureRef;
32 import org.chromium.sdk.util.GenericCallback;
33 import org.chromium.sdk.util.RelaySyncCallback;
34
35 /**
36 * Keeps all current scripts for the debug session and handles script source loa ding.
37 */
38 class WipScriptManager {
39 private final WipTabImpl tabImpl;
40 // Access must be synchronized.
41 private final Map<String, ScriptData> scriptIdToData = new HashMap<String, Scr iptData>();
42
43 /**
44 * A future for script pre-load operation. User may call {@link #getScripts} a t any time,
45 * but we return result only once we have loaded all pre-existing scripts.
46 */
47 private final AsyncFutureRef<Void> scriptsPreloaded;
48
49 /** Accessed from Dispatch thread only. */
50 private ScriptPopulateMode populateMode = new ScriptPopulateMode();
51
52 WipScriptManager(WipTabImpl tabImpl) {
53 this.tabImpl = tabImpl;
54 this.scriptsPreloaded = populateMode.createAndInitMasterFuture();
55 }
56
57 WipTabImpl getTabImpl() {
58 return tabImpl;
59 }
60
61 // Run command in dispatch thread so that no scripts event could happen in the meantime.
62 // TODO: make sure we do not return those scripts that are reported compiled b ut not loaded yet.
63 RelayOk getScripts(final GenericCallback<Collection<Script>> callback,
64 SyncCallback syncCallback) {
65
66 // Async command chain here, wrap syncCallback to guaranteed calling.
67 RelaySyncCallback relay = new RelaySyncCallback(syncCallback);
68
69 // Guard for the step one.
70 final RelaySyncCallback.Guard guardOne = relay.newGuard();
71
72 // Chain commands are in the reverse order.
73
74 // Wait for script pre-load operation and return scripts.
75 final AsyncFuture.Callback<Void> futureCallback = new AsyncFuture.Callback<V oid>() {
76 @Override
77 public void done(Void res) {
78 if (callback != null) {
79 callback.success(getCurrentScripts());
80 }
81 }
82 };
83
84 // Start everything in dispatch thread (otherwise user may be called from th is thread).
85 Runnable mainRunnable = new Runnable() {
86 @Override
87 public void run() {
88 RelayOk relayOk =
89 scriptsPreloaded.getAsync(futureCallback, guardOne.getRelay().getUse rSyncCallback());
90 guardOne.discharge(relayOk);
91 }
92 };
93
94 return tabImpl.getCommandProcessor().runInDispatchThread(mainRunnable,
95 guardOne.asSyncCallback());
96 }
97
98 Script getScript(String scriptId) {
99 ScriptData data;
100 synchronized (scriptIdToData) {
101 data = getSafe(scriptIdToData, scriptId);
102 }
103 if (data == null) {
104 return null;
105 }
106 if (!data.sourceLoadedFuture.isDone()) {
107 return null;
108 }
109 return data.scriptImpl;
110 }
111
112
113 private Collection<Script> getCurrentScripts() {
114 synchronized (scriptIdToData) {
115 List<Script> list = new ArrayList<Script>(scriptIdToData.size());
116 for (ScriptData data : scriptIdToData.values()) {
117 if (data.sourceLoadedFuture.isDone()) {
118 list.add(data.scriptImpl);
119 }
120 }
121 return list;
122 }
123 }
124
125 public void scriptIsReportedParsed(ScriptParsedEventData data) {
126 final String sourceID = data.scriptId();
127
128 String url = data.url();
129 if (url.isEmpty()) {
130 url = null;
131 }
132
133 ScriptBase.Descriptor<String> descriptor = new ScriptBase.Descriptor<String> (Script.Type.NORMAL,
134 sourceID, url, (int) data.startLine(), (int) data.startColumn(), -1);
135 final WipScriptImpl script = new WipScriptImpl(this, descriptor);
136 final ScriptData scriptData = new ScriptData(script);
137
138 synchronized (scriptIdToData) {
139 if (containsKeySafe(scriptIdToData, sourceID)) {
140 throw new IllegalStateException("Already has script with id " + sourceID );
141 }
142 scriptIdToData.put(sourceID, scriptData);
143 }
144
145 scriptData.sourceLoadedFuture.initializeRunning(new SourceLoadOperation(scri pt, sourceID));
146
147 final ScriptPopulateMode populateModeSaved = populateMode;
148
149 AsyncFuture.Callback<Boolean> callback;
150 SyncCallback syncCallback;
151
152 if (populateModeSaved == null) {
153 callback = new AsyncFuture.Callback<Boolean>() {
154 @Override
155 public void done(Boolean res) {
156 tabImpl.getTabListener().getDebugEventListener().scriptLoaded(script);
157 }
158 };
159 syncCallback = null;
160 } else {
161 populateModeSaved.anotherSourceToWait();
162
163 callback = new AsyncFuture.Callback<Boolean>() {
164 @Override
165 public void done(Boolean res) {
166 populateModeSaved.sourceLoaded(res);
167 }
168 };
169 syncCallback = new SyncCallback() {
170 @Override
171 public void callbackDone(RuntimeException e) {
172 populateModeSaved.sourceLoadedSync(e);
173 }
174 };
175 }
176
177 scriptData.sourceLoadedFuture.getAsync(callback, syncCallback);
178 }
179
180 /**
181 * Asynchronously loads script source.
182 */
183 private final class SourceLoadOperation implements AsyncFuture.Operation<Boole an> {
184 private final WipScriptImpl script;
185 private final String sourceID;
186
187 private SourceLoadOperation(WipScriptImpl script, String sourceID) {
188 this.script = script;
189 this.sourceID = sourceID;
190 }
191
192 @Override
193 public RelayOk start(final Callback<Boolean> operationCallback, SyncCallback syncCallback) {
194 GenericCallback<GetScriptSourceData> commandCallback =
195 new GenericCallback<GetScriptSourceData>() {
196 @Override
197 public void success(GetScriptSourceData data) {
198 String source = data.scriptSource();
199 script.setSource(source);
200 operationCallback.done(true);
201 }
202 @Override
203 public void failure(Exception exception) {
204 throw new RuntimeException(exception);
205 }
206 };
207 GetScriptSourceParams params = new GetScriptSourceParams(sourceID);
208 return tabImpl.getCommandProcessor().send(params, commandCallback, syncCal lback);
209 }
210 }
211
212 private class ScriptData {
213 final WipScriptImpl scriptImpl;
214 final AsyncFutureRef<Boolean> sourceLoadedFuture = new AsyncFutureRef<Boolea n>();
215
216 ScriptData(WipScriptImpl scriptImpl) {
217 this.scriptImpl = scriptImpl;
218 }
219 }
220
221 /**
222 * Asynchronously loads all script sources that will be referenced from a new debug context
223 * (from its stack frames).
224 * Must be called from Dispatch thread.
225 */
226 RelayOk loadScriptSourcesAsync(Set<String> ids, ScriptSourceLoadCallback callb ack,
227 SyncCallback syncCallback) {
228 Queue<ScriptData> scripts = new ArrayDeque<ScriptData>(ids.size());
229 Map<String, WipScriptImpl> result = new HashMap<String, WipScriptImpl>(ids.s ize());
230 synchronized (scriptIdToData) {
231 for (String id : ids) {
232 ScriptData data = getSafe(scriptIdToData, id);
233 if (data == null) {
234 // We probably got id of internal script (usually happens when we susp end on breakpoint
235 // thrown from internals).
236 result.put(id, null);
237 continue;
238 }
239 result.put(id, data.scriptImpl);
240 if (!data.sourceLoadedFuture.isDone()) {
241 scripts.add(data);
242 }
243 }
244 }
245
246 // Start a chain of asynchronous operations.
247 // Make sure we call this sync callback sooner or later.
248 RelaySyncCallback relay = new RelaySyncCallback(syncCallback);
249
250 return loadNextScript(scripts, result, callback, relay);
251 }
252
253 interface ScriptSourceLoadCallback {
254 void done(Map<String, WipScriptImpl> loadedScripts);
255 }
256
257 static String convertAlienSourceId(Object sourceIdObj) {
258 if (sourceIdObj instanceof String == false) {
259 throw new IllegalArgumentException("Script id must be string");
260 }
261 return (String) sourceIdObj;
262 }
263
264 // TODO: scripts are loaded in-series; make load parallel instead (to wait les s).
265 private RelayOk loadNextScript(final Queue<ScriptData> scripts,
266 final Map<String, WipScriptImpl> result, final ScriptSourceLoadCallback ca llback,
267 final RelaySyncCallback relay) {
268 final ScriptData data = scripts.poll();
269 if (data == null) {
270 // Terminate the chain of asynchronous loads and pass a result to the call back.
271 RelayOk relayOk;
272 if (callback != null) {
273 callback.done(result);
274 }
275 return relay.finish();
276 }
277
278 // Create a guard for the case that we fail before issuing next #loadNextScr ipt() call.
279 final RelaySyncCallback.Guard guard = relay.newGuard();
280
281 AsyncFuture.Callback<Boolean> futureCallback = new AsyncFuture.Callback<Bool ean>() {
282 @Override
283 public void done(Boolean res) {
284 RelayOk relayOk = loadNextScript(scripts, result, callback, relay);
285 // We successfully relayed responsibility for operationDestructable to n ext async call,
286 // discharge guard.
287 guard.discharge(relayOk);
288 }
289 };
290
291 // The async operation will call a guard even if something failed within the AsyncFuture.
292 return data.sourceLoadedFuture.getAsync(futureCallback, guard.asSyncCallback ());
293 }
294
295 public void pageReloaded() {
296 synchronized (scriptIdToData) {
297 scriptIdToData.clear();
298 }
299 }
300
301 void endPopulateScriptMode() {
302 populateMode.endMode();
303 populateMode = null;
304 }
305
306 /**
307 * Right after initialization we come into 'populate scripts' mode, when back- end
308 * reports about all pre-existing scripts as if they have just been parsed.
309 * <p>
310 * We treat this notifications differently: all these scripts must be returned from
311 * {@link JavascriptVm#getScripts} from the beginning and only truly new scrip ts get
312 * reported via {@link DebugEventListener#scriptLoaded}.
313 * <p>
314 * This means that until 'populate mode' ends (and all sources are loaded),
315 * {@link JavascriptVm#getScripts} call blocks.
316 */
317 private static class ScriptPopulateMode {
318 /**
319 * Future for script preload operation. It completes when all pre-exising sc ripts
320 * are fully loaded (with sources). The operation result value is an array o f
321 * source loading success/failure flags.
322 */
323 private final AsyncFutureMerger<Boolean> populateAndLoadSourcesFuture =
324 new AsyncFutureMerger<Boolean>();
325
326 /**
327 * Reports that 'populate script' mode is finished. However we may still be waiting for
328 * the corresponding script sources.
329 */
330 void endMode() {
331 populateAndLoadSourcesFuture.subOperationDone(null);
332 populateAndLoadSourcesFuture.subOperationDoneSync(null);
333 }
334
335 /**
336 * We learned about another pre-existing script. Now we have to wait for its source.
337 */
338 void anotherSourceToWait() {
339 populateAndLoadSourcesFuture.addSubOperation();
340 }
341
342 void sourceLoaded(Boolean result) {
343 populateAndLoadSourcesFuture.subOperationDone(result);
344 }
345
346 /**
347 * Additional method that completes {@link #sourceLoaded} and used to be com patible with
348 * {@link SyncCallback} paradigm.
349 */
350 void sourceLoadedSync(RuntimeException e) {
351 populateAndLoadSourcesFuture.subOperationDoneSync(e);
352 }
353
354 /**
355 * Creates a 'master operation' future that hides the complex result value
356 * of {@link #populateAndLoadSourcesFuture}. It hides it from Java GC also,
357 * so the {@link ArrayList} gets collected once operation is finished.
358 */
359 AsyncFutureRef<Void> createAndInitMasterFuture() {
360 AsyncFutureRef<Void> asyncFutureRef = new AsyncFutureRef<Void>();
361 asyncFutureRef.initializeRunning(new AsyncFuture.Operation<Void>() {
362 @Override
363 public RelayOk start(final Callback<Void> callback, SyncCallback syncCal lback) {
364 AsyncFuture<?> innerFuture = populateAndLoadSourcesFuture.getFuture();
365 return innerFuture.getAsync(new Callback<Object>() {
366 @Override
367 public void done(Object res) {
368 callback.done(null);
369 }
370 }, syncCallback);
371 }
372 });
373 return asyncFutureRef;
374 }
375 }
376 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698