| 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 |