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

Side by Side Diff: plugins/org.chromium.sdk.wipbackend.wk118685/src/org/chromium/sdk/internal/wip/EvaluateHack.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 java.util.ArrayList;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.concurrent.atomic.AtomicInteger;
11
12 import org.chromium.sdk.JsEvaluateContext;
13 import org.chromium.sdk.JsEvaluateContext.ResultOrException;
14 import org.chromium.sdk.RelayOk;
15 import org.chromium.sdk.SyncCallback;
16 import org.chromium.sdk.internal.wip.WipExpressionBuilder.ValueNameBuilder;
17 import org.chromium.sdk.internal.wip.WipRelayRunner.ProcessException;
18 import org.chromium.sdk.internal.wip.WipRelayRunner.Step;
19 import org.chromium.sdk.internal.wip.WipValueBuilder.SerializableValue;
20 import org.chromium.sdk.internal.wip.protocol.input.runtime.CallFunctionOnData;
21 import org.chromium.sdk.internal.wip.protocol.input.runtime.EvaluateData;
22 import org.chromium.sdk.internal.wip.protocol.input.runtime.RemoteObjectValue;
23 import org.chromium.sdk.internal.wip.protocol.output.WipParamsWithResponse;
24 import org.chromium.sdk.internal.wip.protocol.output.runtime.CallArgumentParam;
25 import org.chromium.sdk.internal.wip.protocol.output.runtime.CallFunctionOnParam s;
26 import org.chromium.sdk.internal.wip.protocol.output.runtime.EvaluateParams;
27 import org.chromium.sdk.util.GenericCallback;
28 import org.chromium.sdk.util.RelaySyncCallback;
29
30 /**
31 * Helper class that implements evaluate with additional context and
32 * destination group id operation. This implementation is a hack because it adds (injects)
33 * a property to the global object and works with its properties. The normal app roach is when
34 * the protocol itself supports this operation. As it hopefully will.
35 */
36 public class EvaluateHack {
37
38 private final WipTabImpl tabImpl;
39 private final AtomicInteger uniqueIdCounter = new AtomicInteger(0);
40 private boolean objectInjected = false;
41
42 public EvaluateHack(WipTabImpl tabImpl) {
43 this.tabImpl = tabImpl;
44 }
45
46 /**
47 * Implements evaluate with additional context and destination group id operat ion.
48 * The implementation modifies a global object.
49 * @param destinationValueLoader value loader that corresponds to the destinat ion group
50 * @param evaluateCommandHandler provides a particular request type
51 */
52 public RelayOk evaluateAsync(String expression, ValueNameBuilder valueNameBuid ler,
53 Map<String, ? extends SerializableValue> additionalContext,
54 WipValueLoader destinationValueLoader, EvaluateCommandHandler<?> evaluateC ommandHandler,
55 final JsEvaluateContext.EvaluateCallback callback, SyncCallback syncCallba ck) {
56
57 RelaySyncCallback relaySyncCallback = new RelaySyncCallback(syncCallback);
58
59 final EvaluateSession evaluateSession = new EvaluateSession(expression, valu eNameBuidler,
60 additionalContext, destinationValueLoader, evaluateCommandHandler);
61
62 final RelaySyncCallback.Guard guard = relaySyncCallback.newGuard();
63
64 GenericCallback<Void> postEnsureCallback = new GenericCallback<Void>() {
65 @Override
66 public void success(Void value) {
67 RelayOk relayOk = evaluateSession.run(callback, guard.getRelay());
68 guard.discharge(relayOk);
69 }
70
71 @Override
72 public void failure(Exception exception) {
73 if (callback != null) {
74 callback.failure(exception);
75 }
76 }
77 };
78
79 return ensureObjectInjected(postEnsureCallback, guard.asSyncCallback());
80 }
81
82 /**
83 * Provides an actual evaluate request. It may or may not refer to a particula r call frame
84 * or deal with other details that are out of scope of this class.
85 *
86 * @param <DATA> type of request's response
87 */
88 public interface EvaluateCommandHandler<DATA> {
89 WipParamsWithResponse<DATA> createRequest(String patchedUserExpression,
90 WipValueLoader destinationValueLoader);
91
92 ResultOrException processResult(DATA response, WipValueLoader destinationVal ueLoader,
93 ValueNameBuilder valueNameBuidler);
94
95 /**
96 * Return the same exception or wraps it with a more high-level error detail s.
97 * @return not null
98 */
99 Exception processFailure(Exception cause);
100 }
101
102 synchronized void pageReloaded() {
103 objectInjected = false;
104 }
105
106 /**
107 * Corresponds to a one evaluate operation. Holds most of parameters. It does following:
108 * <ol>
109 * <li>creates a temporary object inside the main injected object,
110 * <li>puts all values from additional context thus making it a 'with' objec t,
111 * <li>evaluates user expression inside the 'with' operator,
112 * <li>returns result to a user callback,
113 * <li>deletes the temporary object.
114 * </ol>
115 *
116 * It uses {@link WipRelayRunner} as an engine.
117 */
118 private class EvaluateSession {
119 private final String userExpression;
120 private final ValueNameBuilder valueNameBuidler;
121 private final Map<String, ? extends SerializableValue> additionalContext;
122 private final WipValueLoader destinationValueLoader;
123 private final EvaluateCommandHandler<?> evaluateCommandHandler;
124
125 private final String dataId = "d" + uniqueIdCounter.incrementAndGet();
126
127 EvaluateSession(String expression, ValueNameBuilder valueNameBuidler,
128 Map<String, ? extends SerializableValue> additionalContext,
129 WipValueLoader destinationValueLoader, EvaluateCommandHandler<?> evaluat eCommandHandler) {
130 this.userExpression = expression;
131 this.valueNameBuidler = valueNameBuidler;
132 this.additionalContext = additionalContext;
133 this.destinationValueLoader = destinationValueLoader;
134 this.evaluateCommandHandler = evaluateCommandHandler;
135 }
136
137 RelayOk run(final JsEvaluateContext.EvaluateCallback callback, RelaySyncCall back relay) {
138 WipRelayRunner.Step<ResultOrException> step = createFillDataObjectStep();
139
140 GenericCallback<ResultOrException> innerCallback;
141 if (callback == null) {
142 innerCallback = null;
143 } else {
144 innerCallback = new GenericCallback<ResultOrException>() {
145 @Override public void success(ResultOrException value) {
146 callback.success(value);
147 }
148 @Override public void failure(Exception exception) {
149 callback.failure(exception);
150 }
151 };
152 }
153
154 return WipRelayRunner.run(tabImpl.getCommandProcessor(), step,
155 innerCallback, relay);
156 }
157
158 /**
159 * Sends request that create a temporary object and fills it with user value s.
160 * User values are passed as 1. 'this', 2. additional arguments to the funct ion.
161 */
162 private WipRelayRunner.Step<ResultOrException> createFillDataObjectStep() {
163 if (additionalContext.isEmpty()) {
164 throw new IllegalArgumentException("Empty context");
165 }
166
167 StringBuilder assigmentBuilder = new StringBuilder();
168 StringBuilder parametersBuilder = new StringBuilder();
169
170 String thisObjectId = null;
171 final List<CallArgumentParam> additionalObjectIds = new ArrayList<CallArgu mentParam>(0);
172 String tempObjectRef = GLOBAL_VARIABLE_NAME + ".data." + dataId + ".";
173 for (Map.Entry<String, ? extends SerializableValue> entry : additionalCont ext.entrySet()) {
174 SerializableValue jsValueBase = entry.getValue();
175 String commandParamName;
176 if (thisObjectId == null && jsValueBase.getRefId() != null) {
177 commandParamName = "this";
178 thisObjectId = jsValueBase.getRefId();
179 } else {
180 commandParamName = "p" + additionalObjectIds.size();
181 CallArgumentParam callArgumentParam = jsValueBase.createCallArgumentPa ram();
182 if (callArgumentParam == null) {
183 throw new IllegalArgumentException("Cannot serialize additional cont ext property " +
184 entry.getKey());
185 }
186 additionalObjectIds.add(callArgumentParam);
187 if (parametersBuilder.length() != 0) {
188 parametersBuilder.append(", ");
189 }
190 parametersBuilder.append(commandParamName);
191 }
192 assigmentBuilder.append(tempObjectRef + entry.getKey() + " = " + command ParamName + ";\n");
193 }
194 if (thisObjectId == null) {
195 // TODO: remove this limitation in protocol.
196 throw new IllegalArgumentException("At least one additional parameter mu st be an object");
197 }
198
199 final String functionText = "function(" + parametersBuilder + ") { " +
200 GLOBAL_VARIABLE_NAME + ".data." + dataId + " = {};\n" +
201 assigmentBuilder + "}";
202
203 final String thisObjectIdFinal = thisObjectId;
204
205 return new WipRelayRunner.SendStepWithResponse<CallFunctionOnData, ResultO rException>() {
206 @Override
207 public WipParamsWithResponse<CallFunctionOnData> getParams() {
208 List<CallArgumentParam> arguments;
209 if (additionalObjectIds.isEmpty()) {
210 arguments = null;
211 } else {
212 arguments = additionalObjectIds;
213 }
214 return new CallFunctionOnParams(thisObjectIdFinal, functionText, argum ents, null, true);
215 }
216
217 @Override
218 public Step<ResultOrException> processResponse(CallFunctionOnData respon se) {
219 if (response.wasThrown() == Boolean.TRUE) {
220 return createHandleErrorStep(response.result());
221 }
222 return createEvaluateStep(evaluateCommandHandler);
223 }
224
225 @Override
226 public Exception processFailure(Exception cause) {
227 return cause;
228 }
229 };
230 }
231
232 private <EVAL_DATA> WipRelayRunner.Step<ResultOrException> createEvaluateSte p(
233 final EvaluateCommandHandler<EVAL_DATA> commandHandler) {
234 return new WipRelayRunner.SendStepWithResponse<EVAL_DATA, ResultOrExceptio n>() {
235 @Override
236 public WipParamsWithResponse<EVAL_DATA> getParams() {
237 String script = "with (" + GLOBAL_VARIABLE_NAME + ".data." + dataId +
238 ") { return (" + userExpression + "); }";
239 String wrappedExpression = "(function() {" + script +"})()";
240
241 WipParamsWithResponse<EVAL_DATA> paramsWithResponse = commandHandler.c reateRequest(
242 wrappedExpression, destinationValueLoader);
243
244 return paramsWithResponse;
245 }
246
247 @Override
248 public Step<ResultOrException> processResponse(EVAL_DATA response) {
249 ResultOrException resultOrException =
250 commandHandler.processResult(response, destinationValueLoader, val ueNameBuidler);
251
252 clearTempObjectAsync();
253
254 return WipRelayRunner.createFinalStep(resultOrException);
255 }
256
257 @Override
258 public Exception processFailure(Exception cause) {
259 return commandHandler.processFailure(cause);
260 }
261 };
262 }
263
264 /**
265 * Clears the temporary object. It is done asynchronously, outside the main relay, because
266 * user shouldn't wait for its result.
267 */
268 private void clearTempObjectAsync() {
269 String script = "delete " + GLOBAL_VARIABLE_NAME + ".data." + dataId + ";" ;
270 String deleteDataExpression = "(function() {" + script +"})()";
271 EvaluateParams evaluateParams =
272 new EvaluateParams(deleteDataExpression, null, null, null, null, true) ;
273 tabImpl.getCommandProcessor().send(evaluateParams, (WipCommandCallback) nu ll, null);
274 }
275
276 /**
277 * An alternative spin-off in the relay, that handles an exception we ran in to.
278 * The additional step is needed because the exception message is only avail able from
279 * its 'message' pseudo-property (a getter).
280 */
281 private Step<ResultOrException> createHandleErrorStep(final RemoteObjectValu e remoteObjectValue) {
282 return new WipRelayRunner.SendStepWithResponse<CallFunctionOnData, ResultO rException>() {
283 @Override
284 public WipParamsWithResponse<CallFunctionOnData> getParams() {
285 String functionText = "function() { return String(this.message); }";
286 return new CallFunctionOnParams(remoteObjectValue.objectId(), function Text, null, null, true);
287 }
288
289 @Override
290 public Step<ResultOrException> processResponse(CallFunctionOnData respon se)
291 throws ProcessException {
292 throw new ProcessException("Helper script failed on remote: " +
293 response.result().value());
294 }
295
296 @Override
297 public Exception processFailure(Exception cause) {
298 return cause;
299 }
300 };
301 }
302 }
303
304 /**
305 * Makes sure that we injected a helper object inside a global object.
306 * This cannot be implemented as step in {@link WipRelayRunner}, because the m ethod
307 * is synchronized and cannot undergo required control inversion.
308 */
309 private synchronized RelayOk ensureObjectInjected(GenericCallback<Void> callba ck,
310 SyncCallback syncCallback) {
311 if (objectInjected) {
312 callback.success(null);
313 return RelaySyncCallback.finish(syncCallback);
314 } else {
315 objectInjected = true;
316 return injectObject(callback, syncCallback);
317 }
318 }
319
320 private RelayOk injectObject(final GenericCallback<Void> callback, SyncCallbac k syncCallback) {
321 // 'data' is for temporary objects.
322 // 'code' is for utility methods.
323 String injectedObjectText = "{ data: {}, code: {}}";
324 String expression = "(function() { " + GLOBAL_VARIABLE_NAME + " = " + inject edObjectText +
325 " ; })()";
326
327 EvaluateParams evaluateParams = new EvaluateParams(expression, null, false, null, null, true);
328
329 GenericCallback<EvaluateData> wrappedCallback = new GenericCallback<Evaluate Data>() {
330 @Override
331 public void success(EvaluateData value) {
332 // TODO: check result.
333 callback.success(null);
334 }
335
336 @Override
337 public void failure(Exception exception) {
338 callback.failure(new Exception("Failed to inject evaluate helper script into remote VM",
339 exception));
340 }
341 };
342
343 return tabImpl.getCommandProcessor().send(evaluateParams, wrappedCallback, s yncCallback);
344 }
345
346 private static final String GLOBAL_VARIABLE_NAME = "_com_chromium_debug_helper ";
347 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698