OLD | NEW |
| (Empty) |
1 # Using the Chrome Devtools JavaScript preprocessing feature | |
2 | |
3 The Chrome Devtools JavaScript preprocessor intercepts JavaScript just before it | |
4 enters V8, the Chrome JS system, allowing the JS to be transcoded before | |
5 compilation. In combination with page injected JavaScript, the preprocessor | |
6 allows a complete synthetic runtime to be constructed in JavaScript. Combined | |
7 with other functions in the `chrome.devtools` extension API, the preprocessor | |
8 allows new more sophisticated JavaScript-related developer tools to be created. | |
9 | |
10 ## API | |
11 | |
12 To use the script preprocessor, write a | |
13 [chrome devtools extension](http://developer.chrome.com/extensions/devtools.insp
ectedWindow.html#method-reload) | |
14 that reloads the Web page with the preprocessor installed: | |
15 | |
16 ```javascript | |
17 chrome.devtools.inspectedWindow.reload({ | |
18 ignoreCache: true, | |
19 injectedScript: runThisFirst, | |
20 preprocessorScript: preprocessor | |
21 }); | |
22 ``` | |
23 | |
24 where `preprocessorScript` is source code (string) for a JavaScript function | |
25 taking three string arguments, the source to preprocess, the URL of the source, | |
26 and a function name if the source is an DOM event handler. The | |
27 `preprocessorerScript` function should return a string to be compiled by Chrome | |
28 in place of the input source. In the case that the source is a DOM event | |
29 handler, the returned source must compile to a single JS function. | |
30 | |
31 The | |
32 [Chrome Preprocessor Example](http://developer.chrome.com/extensions/samples.htm
l) | |
33 illustrates the API call in a simple chrome devtools extension. Download and | |
34 unpack the .zip file, use `chrome://extensions` in Developer Mode and load the | |
35 unpacked extension. Then open or reopen devtools. The Preprocessor panel has a | |
36 **reload** button that triggers a simple preprocessor. | |
37 | |
38 The preprocessor runs in an isolated world similar to the environment of Chrome | |
39 content scripts. A `window` object is available but it shares no properties with | |
40 the Web page `window` object. DOM calls in the preprocessor environment will | |
41 operate on the Web page, but developers should be cautious about operating on | |
42 the DOM in the preprocessor. We do not test such operations though we expect the | |
43 result to resemble calls from the outer function of `<script>` tags. | |
44 | |
45 In some applications the developer may coordinate runtime initialization using | |
46 the `injectedScript` property in the object passed to the `reload()` call. This | |
47 is also JavaScript source code; it is compiled into the page ahead of any Web | |
48 page scripts and thus before any JavaScript is preprocessed. | |
49 | |
50 The preprocessor is compiled once just before the first JavaScript appears. It | |
51 remains active until the page is reloaded or otherwise navigated. Navigating the | |
52 Web page back and then forward will result in no preprocessing. Closing devtools | |
53 will leave the preprocessor in place. | |
54 | |
55 ## Use Cases | |
56 | |
57 The script preprocessor supports transcoding input source to JavaScript. Use cas
es include: | |
58 | |
59 * Adding write barriers for Querypoint debugging, | |
60 * Supporting feature-specific debugging of next generation EcmaScript using eg
Traceur, | |
61 * Integration of development tools like coverage analysis. | |
62 * Analysis of call sequences for performance tuning. | |
63 | |
64 Several JavaScript compilers support transcoding, including | |
65 [Traceur](https://github.com/google/traceur-compiler#readme) and | |
66 [Esprima](http://esprima.org/). | |
67 | |
68 ## Implementation | |
69 | |
70 The implementation relies on the Devtools front-end hosting an extension | |
71 supplying the preprocessor script; the front end communicates with the browser | |
72 backend over eg web sockets. | |
73 | |
74 The devtools extension function call issues a postMessage() event from the | |
75 devtools extension iframe to the devtools main frame. The event is handled in | |
76 `ExtensionServer.js` which forwards it over the | |
77 [devtools remote debug protocol](https://developers.google.com/chrome-developer-
tools/docs/protocol/1.0/page#command-reload). | |
78 (See [Bug 229971](https://crbug.com/229971) for this part of the implementation | |
79 and its status). | |
80 | |
81 When the preprocessor script arrives in the back end, | |
82 `InspectorPageAgent::reload` stores the preprocessor script in | |
83 `m_pendingScriptPreprocessor`. After the browser begins the reload operation, it | |
84 calls `PageDebuggerAgent::didClearWindowObjectInWorld` which moves the processor | |
85 source into the `scriptDebugServer()`. | |
86 | |
87 Next the browser prepares the page environment and calls | |
88 `PageDebuggerAgent::didClearWindowObjectInWorld`. This function clears the | |
89 preprocessor object pointer and if it is not recreated during the page load, no | |
90 scripts will be preprocessed. At this point we only store the preprocessor | |
91 source, delaying the compilation of the preprocessor until just before its first | |
92 use. This helps ensure that the JS environment we use is fully initialized. | |
93 | |
94 Source to be preprocessed comes from three different places: | |
95 | |
96 1. Web page `<script>` tags, | |
97 1. DOM event-listener attributes, eg `onload`, | |
98 1. JS `eval()` or `new Function()` calls. | |
99 | |
100 When the browser encounters either a `<script>` tag | |
101 (`ScriptController::executeScriptInMainWorld`) or an element attribute script | |
102 (`V8LazyEventListener::prepareListenerObject`) we call a corresponding function | |
103 in InspectorInstrumentation. This function has a fast inlined return path in the | |
104 case that the debugger is not attached. | |
105 | |
106 If the debugger is attached, InspectorInstrumentation will call the matching | |
107 function in PageDebuggerAgent (see core/inspector/InspectorInstrumentation.idl). | |
108 It checks to see if the preprocessor is installed. If not, it returns. | |
109 | |
110 The preprocessor source is stored in PageScriptDebugServer. | |
111 If the preprocessor is installed, we check to see if it is compiled. If not, we | |
112 create a new `ScriptPreprocessor` object. The constructor uses | |
113 `ScriptController::executeScriptInIsolatedWorld` to compile the preprocessor in | |
114 a new isolated world associated with the Web page's main world. If the | |
115 compilation and outer script execution succeed and if the result is a JavaScript | |
116 function, we store the resulting function as a `ScopedPersistent<v8::Function>` | |
117 member of the preprocessor. | |
118 | |
119 If the `PageScriptDebugServer::preprocess()` has a value for the preprocessor | |
120 function, it applies the function to the web page source using | |
121 `V8ScriptRunner::callAsFunction()`. This calls the compiled JS function in the | |
122 ScriptPreprocessor's isolated world and retrieves the resulting string. | |
123 | |
124 When the preprocessed JavaScript source runs it may call `eval()` or | |
125 `new Function()`. These calls cause the V8 runtime to compile source. | |
126 Immediately before compiling, V8 issues a beforeCompile event which triggers | |
127 `ScriptDebugServer::handleV8DebugEvent()`. This code is only called if the | |
128 debugger is active. In the handler we call `ScriptDebugServer::preprocessEval()` | |
129 to examine the ScriptCompilationTypeInfo, a marker set by V8, to see if we are | |
130 compiling dynamic code. Only dynamic code is preprocessed in this function and | |
131 only if we are not executing the preprocessor itself. | |
132 | |
133 During the browser operation, API generation code, debugger console | |
134 initialization code, injected page script code, debugger information extraction | |
135 code, and regular web page code enter this function. There is currently no way | |
136 to distinguish internal or system code from the web page code. However the | |
137 internal code is all static. By limiting our preprocessing to dynamic code in | |
138 the beforeCompile handler, we know we are only operating on Web page code. The | |
139 static Web page code is preprocessed as described above. | |
140 | |
141 ## Limitations | |
142 | |
143 We currently do not support preprocessing of WebWorker source code. | |
OLD | NEW |