blob: 7b1b9aec450a37880dc4a0f577895a7f092b51be [file] [log] [blame] [view]
andybons3322f762015-08-24 21:37:091# Using the Chrome Devtools JavaScript preprocessing feature
2
andybonsad92aa32015-08-31 02:27:443The Chrome Devtools JavaScript preprocessor intercepts JavaScript just before it
4enters V8, the Chrome JS system, allowing the JS to be transcoded before
5compilation. In combination with page injected JavaScript, the preprocessor
6allows a complete synthetic runtime to be constructed in JavaScript. Combined
7with other functions in the `chrome.devtools` extension API, the preprocessor
8allows new more sophisticated JavaScript-related developer tools to be created.
andybons3322f762015-08-24 21:37:099
10## API
11
andybonsad92aa32015-08-31 02:27:4412To use the script preprocessor, write a
13[chrome devtools extension](http://developer.chrome.com/extensions/devtools.inspectedWindow.html#method-reload)
14that reloads the Web page with the preprocessor installed:
15
16```javascript
andybons3322f762015-08-24 21:37:0917chrome.devtools.inspectedWindow.reload({
andybonsad92aa32015-08-31 02:27:4418 ignoreCache: true,
andybons3322f762015-08-24 21:37:0919 injectedScript: runThisFirst,
20 preprocessorScript: preprocessor
21});
22```
andybons3322f762015-08-24 21:37:0923
andybonsad92aa32015-08-31 02:27:4424where `preprocessorScript` is source code (string) for a JavaScript function
25taking three string arguments, the source to preprocess, the URL of the source,
26and a function name if the source is an DOM event handler. The
27`preprocessorerScript` function should return a string to be compiled by Chrome
28in place of the input source. In the case that the source is a DOM event
29handler, the returned source must compile to a single JS function.
andybons3322f762015-08-24 21:37:0930
andybonsad92aa32015-08-31 02:27:4431The
32[Chrome Preprocessor Example](http://developer.chrome.com/extensions/samples.html)
33illustrates the API call in a simple chrome devtools extension. Download and
34unpack the .zip file, use `chrome://extensions` in Developer Mode and load the
35unpacked extension. Then open or reopen devtools. The Preprocessor panel has a
36**reload** button that triggers a simple preprocessor.
andybons3322f762015-08-24 21:37:0937
andybonsad92aa32015-08-31 02:27:4438The preprocessor runs in an isolated world similar to the environment of Chrome
39content scripts. A `window` object is available but it shares no properties with
40the Web page `window` object. DOM calls in the preprocessor environment will
41operate on the Web page, but developers should be cautious about operating on
42the DOM in the preprocessor. We do not test such operations though we expect the
43result to resemble calls from the outer function of `<script>` tags.
andybons3322f762015-08-24 21:37:0944
andybonsad92aa32015-08-31 02:27:4445In some applications the developer may coordinate runtime initialization using
46the `injectedScript` property in the object passed to the `reload()` call. This
47is also JavaScript source code; it is compiled into the page ahead of any Web
48page scripts and thus before any JavaScript is preprocessed.
49
50The preprocessor is compiled once just before the first JavaScript appears. It
51remains active until the page is reloaded or otherwise navigated. Navigating the
52Web page back and then forward will result in no preprocessing. Closing devtools
53will leave the preprocessor in place.
andybons3322f762015-08-24 21:37:0954
55## Use Cases
56
57The script preprocessor supports transcoding input source to JavaScript. Use cases include:
andybonsad92aa32015-08-31 02:27:4458
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
64Several JavaScript compilers support transcoding, including
65[Traceur](https://github.com/google/traceur-compiler#readme) and
66[Esprima](http://esprima.org/).
andybons3322f762015-08-24 21:37:0967
68## Implementation
69
andybonsad92aa32015-08-31 02:27:4470The implementation relies on the Devtools front-end hosting an extension
71supplying the preprocessor script; the front end communicates with the browser
72backend over eg web sockets.
andybons3322f762015-08-24 21:37:0973
andybonsad92aa32015-08-31 02:27:4474The devtools extension function call issues a postMessage() event from the
75devtools 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
79and its status).
andybons3322f762015-08-24 21:37:0980
andybonsad92aa32015-08-31 02:27:4481When 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
84calls `PageDebuggerAgent::didClearWindowObjectInWorld` which moves the processor
85source into the `scriptDebugServer()`.
andybons3322f762015-08-24 21:37:0986
andybonsad92aa32015-08-31 02:27:4487Next the browser prepares the page environment and calls
88`PageDebuggerAgent::didClearWindowObjectInWorld`. This function clears the
89preprocessor object pointer and if it is not recreated during the page load, no
90scripts will be preprocessed. At this point we only store the preprocessor
91source, delaying the compilation of the preprocessor until just before its first
92use. This helps ensure that the JS environment we use is fully initialized.
andybons3322f762015-08-24 21:37:0993
94Source to be preprocessed comes from three different places:
andybons3322f762015-08-24 21:37:0995
andybonsad92aa32015-08-31 02:27:44961. Web page `<script>` tags,
971. DOM event-listener attributes, eg `onload`,
981. JS `eval()` or `new Function()` calls.
andybons3322f762015-08-24 21:37:0999
andybonsad92aa32015-08-31 02:27:44100When the browser encounters either a `<script>` tag
101(`ScriptController::executeScriptInMainWorld`) or an element attribute script
102(`V8LazyEventListener::prepareListenerObject`) we call a corresponding function
103in InspectorInstrumentation. This function has a fast inlined return path in the
104case that the debugger is not attached.
105
106If the debugger is attached, InspectorInstrumentation will call the matching
107function in PageDebuggerAgent (see core/inspector/InspectorInstrumentation.idl).
108It checks to see if the preprocessor is installed. If not, it returns.
andybons3322f762015-08-24 21:37:09109
110The preprocessor source is stored in PageScriptDebugServer.
andybonsad92aa32015-08-31 02:27:44111If the preprocessor is installed, we check to see if it is compiled. If not, we
112create a new `ScriptPreprocessor` object. The constructor uses
113`ScriptController::executeScriptInIsolatedWorld` to compile the preprocessor in
114a new isolated world associated with the Web page's main world. If the
115compilation and outer script execution succeed and if the result is a JavaScript
116function, we store the resulting function as a `ScopedPersistent<v8::Function>`
117member of the preprocessor.
andybons3322f762015-08-24 21:37:09118
andybonsad92aa32015-08-31 02:27:44119If the `PageScriptDebugServer::preprocess()` has a value for the preprocessor
120function, it applies the function to the web page source using
121`V8ScriptRunner::callAsFunction()`. This calls the compiled JS function in the
122ScriptPreprocessor's isolated world and retrieves the resulting string.
andybons3322f762015-08-24 21:37:09123
andybonsad92aa32015-08-31 02:27:44124When the preprocessed JavaScript source runs it may call `eval()` or
125`new Function()`. These calls cause the V8 runtime to compile source.
126Immediately before compiling, V8 issues a beforeCompile event which triggers
127`ScriptDebugServer::handleV8DebugEvent()`. This code is only called if the
128debugger is active. In the handler we call `ScriptDebugServer::preprocessEval()`
129to examine the ScriptCompilationTypeInfo, a marker set by V8, to see if we are
130compiling dynamic code. Only dynamic code is preprocessed in this function and
131only if we are not executing the preprocessor itself.
andybons3322f762015-08-24 21:37:09132
andybonsad92aa32015-08-31 02:27:44133During the browser operation, API generation code, debugger console
134initialization code, injected page script code, debugger information extraction
135code, and regular web page code enter this function. There is currently no way
136to distinguish internal or system code from the web page code. However the
137internal code is all static. By limiting our preprocessing to dynamic code in
138the beforeCompile handler, we know we are only operating on Web page code. The
139static Web page code is preprocessed as described above.
andybons3322f762015-08-24 21:37:09140
141## Limitations
142
andybonsad92aa32015-08-31 02:27:44143We currently do not support preprocessing of WebWorker source code.